diff --git a/.github/workflows/paper-ci.yml b/.github/workflows/paper-ci.yml deleted file mode 100644 index fd90967..0000000 --- a/.github/workflows/paper-ci.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: CI Papers - -on: - push: - branches: [ "main", "develop"] - pull_request: - branches: [ "main", "develop"] - -jobs: - run-full-tests: - - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest] - python-version: ["3.9", "3.10", "3.11", "3.12"] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m ensurepip --upgrade - python -m pip install --upgrade setuptools - pip install -r requirements.txt - pip install -e . - - name: Test with pytest on main branch (an example from Technometrics paper) - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' - run: | - python ./examples/Technometrics2024/figure1b.py - - - name: Test with pytest on main branch (an example from IJOC paper) - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' - run: | - python ./examples/IJOC2024+/ci_example.py -funcname "holder" diff --git a/.github/workflows/puq-ci.yml b/.github/workflows/puq-ci.yml index 580c37c..481d6b0 100644 --- a/.github/workflows/puq-ci.yml +++ b/.github/workflows/puq-ci.yml @@ -15,8 +15,8 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest] - python-version: ["3.9", "3.10", "3.11", "3.12"] + os: [windows-latest, ubuntu-latest, macos-latest] + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v2 @@ -27,10 +27,12 @@ jobs: - name: Install dependencies run: | python -m ensurepip --upgrade - python -m pip install --upgrade setuptools + python -m pip install --upgrade pip setuptools wheel + pip install numpy pip install -r requirements.txt + python -m pip install git+https://github.com/davidogara/hetGPy.git python -m pip install flake8 pytest pytest-cov Cython pip install -e . - name: Test with pytest run: | - ./tests/run-tests.sh + pytest tests/ diff --git a/.wordlist.txt b/.wordlist.txt index 557638f..f8859aa 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -69,3 +69,8 @@ jpg matyas ensurepip setuptools +O'Gara +davidogara +designmethods +hetGPy +surrogatemethods diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eb567e6..aaf156e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,19 @@ Below are the release notes for PUQ. May reference issues on: https://github.com/parallelUQ/PUQ/issues +Release 0.1.1 +------------- + +:Date: Oct 8, 2025 + +- :code:`surrogatemethods` have been updated to use the `hetGPy `_ package as a base. +- :code:`surrogatemethods` provide four emulators for deterministic, stochastic, and one- or multi-dimensional outputs. +- :code:`designmethods` provide four sequential design procedures. +- :code:`examples` have been revised to include illustrative cases from five different papers. +- :code:`tests` include new checks for both emulators and design methods. + + + Release 0.1.0 ------------- diff --git a/LICENSE b/LICENSE index e95030b..ec4bcdc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 PUQ Development Team +Copyright (c) 2025 PUQ Development Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PUQ/design.py b/PUQ/design.py deleted file mode 100644 index 1d0e8f2..0000000 --- a/PUQ/design.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -This module contains a class that implements the main design method. -""" - -import importlib - - -class designer(object): - def __init__(self, data_cls=None, method="SEQCAL", args={}): - self.method = importlib.import_module("PUQ.designmethods." + method) - self._info = {"method": method} - - self.fit(data_cls, args) - - def fit(self, data_cls, args): - self.method.fit(self._info, data_cls, args) diff --git a/PUQ/designmethods/SEQCAL.py b/PUQ/designmethods/SEQCAL.py deleted file mode 100644 index 337d44b..0000000 --- a/PUQ/designmethods/SEQCAL.py +++ /dev/null @@ -1,269 +0,0 @@ -import numpy as np -from PUQ.designmethods.gen_funcs.acquisition_funcs import ( - maxvar, - eivar, - maxexp, - rnd, - imse, -) -from PUQ.designmethods.SEQCALsupport import ( - fit_emulator, - load_H, - update_arrays, - create_arrays, - pad_arrays, - select_condition, - rebuild_condition, -) -from libensemble.message_numbers import ( - STOP_TAG, - PERSIS_STOP, - FINISHED_PERSISTENT_GEN_TAG, - EVAL_GEN_TAG, -) -from libensemble.tools.persistent_support import PersistentSupport -from libensemble.alloc_funcs.start_only_persistent import ( - only_persistent_gens as alloc_f, -) -from libensemble.libE import libE -from libensemble.tools import parse_args, save_libE_output, add_unique_random_streams -from PUQ.posterior import posterior - - -def fit(fitinfo, data_cls, args): - mini_batch = args["mini_batch"] - n_init_thetas = args["n_init_thetas"] - nworkers = args["nworkers"] - AL_type = args["AL"] - seed_n0 = args["seed_n0"] - prior = args["prior"] - max_evals = args["max_evals"] - test_data = args["data_test"] - - out = data_cls.out - sim_f = data_cls.sim - - sim_specs = { - "sim_f": sim_f, - "in": ["thetas"], - "out": out, - "user": {"function": data_cls.function}, - } - - gen_out = [ - ("thetas", float, data_cls.p), - ("priority", int), - ("obs", float, (1,)), - ("obsvar", float, (1,)), - ("TV", float), - ("HD", float), - ] - - gen_specs = { - "gen_f": gen_f, - "persis_in": [o[0] for o in gen_out] + ["f", "sim_id"], - "out": gen_out, - "user": { - "n_init_thetas": n_init_thetas, # Num thetas in initial batch - "mini_batch": mini_batch, # No. of thetas to generate per step - "nworkers": nworkers, - "AL": AL_type, - "seed_n0": seed_n0, - "synth_cls": data_cls, - "test_data": test_data, - "prior": prior, - "type_init": args["type_init"], - }, - } - - alloc_specs = { - "alloc_f": alloc_f, - "user": { - "init_sample_size": 0, - "async_return": True, # True = Return results to gen as they come in (after sample) - "active_recv_gen": True, # Persistent gen can handle irregular communications - }, - } - libE_specs = {"nworkers": nworkers, "comms": "local"} - # libE_specs = {'nworkers': nworkers, 'comms': 'local', 'sim_dirs_make': True, - # 'sim_dir_copy_files': [os.path.join(os.getcwd(), '48Ca_template.in')]} - - persis_info = add_unique_random_streams({}, nworkers + 1) - - # Currently just allow gen to exit if mse goes below threshold value - exit_criteria = {"sim_max": max_evals} # Now just a set number of sims. - - # Perform the run - H, persis_info, flag = libE( - sim_specs, - gen_specs, - exit_criteria, - persis_info, - alloc_specs=alloc_specs, - libE_specs=libE_specs, - ) - - fitinfo["f"] = H["f"] - fitinfo["theta"] = H["thetas"] - fitinfo["TV"] = H["TV"] - fitinfo["HD"] = H["HD"] - return - - -def gen_f(H, persis_info, gen_specs, libE_info): - """Generator to select and obviate parameters for calibration.""" - ps = PersistentSupport(libE_info, EVAL_GEN_TAG) - rand_stream = persis_info["rand_stream"] - n0 = gen_specs["user"]["n_init_thetas"] - mini_batch = gen_specs["user"]["mini_batch"] - n_workers = gen_specs["user"]["nworkers"] - AL = gen_specs["user"]["AL"] - seed = gen_specs["user"]["seed_n0"] - synth_info = gen_specs["user"]["synth_cls"] - test_data = gen_specs["user"]["test_data"] - prior_func = gen_specs["user"]["prior"] - type_init = gen_specs["user"]["type_init"] - - obsvar = synth_info.obsvar - data = synth_info.real_data - theta_limits = synth_info.thetalimits - - thetatest, posttest, ftest, priortest = None, None, None, None - if test_data is not None: - thetatest, posttest, ftest, priortest = ( - test_data["theta"], - test_data["p"], - test_data["f"], - test_data["p_prior"], - ) - - true_fevals = np.reshape(data[0, :], (1, data.shape[1])) - n_x = synth_info.d - x = synth_info.x - real_x = synth_info.real_x - - obs_offset, theta_offset, generated_no = 0, 0, 0 - TV, HD = 1000, 1000 - fevals, pending, prev_pending, complete, prev_complete = ( - None, - None, - None, - None, - None, - ) - first_iter = True - tag = 0 - update_model = False - acquisition_f = eval(AL) - list_id = [] - - theta = 0 - - while tag not in [STOP_TAG, PERSIS_STOP]: - if not first_iter: - # Update fevals from calc_in - update_arrays( - n_x, - fevals, - pending, - complete, - calc_in, - obs_offset, - theta_offset, - list_id, - ) - update_model = rebuild_condition( - complete, prev_complete, n_theta=mini_batch, n_initial=n0 - ) - - if not update_model: - tag, Work, calc_in = ps.recv() - if tag in [STOP_TAG, PERSIS_STOP]: - break - - if update_model: - # print('Updating model...\n') - - # print('Percentage Pending: %0.2f ( %d / %d)' % (100*np.round(np.mean(pending), 4), - # np.sum(pending), - # np.prod(pending.shape))) - # print('Percentage Complete: %0.2f ( %d / %d)' % (100*np.round(np.mean(complete), 4), - # np.sum(complete), - # np.prod(pending.shape))) - - emu = fit_emulator(x, theta, fevals, theta_limits) - prev_pending = pending.copy() - update_model = False - - # Obtain the accuracy on the test set - if test_data is not None: - post = posterior(data_cls=synth_info, emulator=emu) - posttesthat, posttestvar = post.predict(thetatest) - TV = np.mean(np.abs(posttest - posttesthat * priortest)) - HD = np.sqrt( - 0.5 * np.mean((np.sqrt(posttesthat) - np.sqrt(posttest)) ** 2) - ) - - if first_iter: - # print('Selecting theta for the first iteration...\n') - - n_init = max(n_workers - 1, n0) - theta = prior_func.rnd(n_init, seed) - - fevals, pending, prev_pending, complete, prev_complete = create_arrays( - n_x, n_init - ) - - H_o = np.zeros(len(theta), dtype=gen_specs["out"]) - H_o = load_H(H_o, theta, TV, HD, generated_no, set_priorities=True) - tag, Work, calc_in = ps.send_recv(H_o) - first_iter = False - generated_no += n_workers - 1 - - else: - if select_condition( - complete, prev_complete, n_theta=mini_batch, n_initial=n0 - ): - # print('Selecting theta...\n') - - prev_complete = complete.copy() - new_theta = acquisition_f( - mini_batch, - x, - real_x, - emu, - theta, - fevals, - true_fevals, - obsvar, - theta_limits, - prior_func, - thetatest, - priortest, - type_init, - ) - - ( - theta, - fevals, - pending, - prev_pending, - complete, - prev_complete, - ) = pad_arrays( - n_x, - new_theta, - theta, - fevals, - pending, - prev_pending, - complete, - prev_complete, - ) - - H_o = np.zeros(len(new_theta), dtype=gen_specs["out"]) - H_o = load_H(H_o, new_theta, TV, HD, generated_no, set_priorities=True) - tag, Work, calc_in = ps.send_recv(H_o) - generated_no += mini_batch - - return None, persis_info, FINISHED_PERSISTENT_GEN_TAG diff --git a/PUQ/designmethods/SEQCALOPT.py b/PUQ/designmethods/SEQCALOPT.py deleted file mode 100644 index 2cc258d..0000000 --- a/PUQ/designmethods/SEQCALOPT.py +++ /dev/null @@ -1,390 +0,0 @@ -import numpy as np -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - get_emuvar, - multiple_pdfs, -) -from PUQ.designmethods.gen_funcs.EIVAR import eivar -from PUQ.designmethods.gen_funcs.MAXEXP import maxexp -from PUQ.designmethods.gen_funcs.MAXVAR import maxvar -from PUQ.designmethods.gen_funcs.EI import ei -from PUQ.designmethods.gen_funcs.PI import pi -from PUQ.designmethods.gen_funcs.HYBRID_EI import hybrid_ei -from PUQ.designmethods.gen_funcs.RND import rnd -from PUQ.designmethods.SEQCALsupport import ( - fit_emulator, - load_H_opt, - update_arrays, - create_arrays, - pad_arrays, - rebuild_condition_opt, -) -from libensemble.message_numbers import ( - STOP_TAG, - PERSIS_STOP, - FINISHED_PERSISTENT_GEN_TAG, - EVAL_GEN_TAG, -) -from libensemble.tools.persistent_support import PersistentSupport -from libensemble.alloc_funcs.start_only_persistent import ( - only_persistent_gens as alloc_f, -) -from libensemble.libE import libE -from libensemble.tools import parse_args, save_libE_output, add_unique_random_streams -from smt.sampling_methods import LHS -from PUQ.posterior import posterior -import time - - -def fit(fitinfo, data_cls, args): - - mini_batch = args["mini_batch"] - nworkers = args["nworkers"] - AL_type = args["AL"] - seed_n0 = args["seed_n0"] - prior = args["prior"] - max_evals = args["max_evals"] - test_data = args["data_test"] - - if AL_type in ["ei", "pi", "hybrid_ei", "hybrid_pi", "eivar"]: - candsize = args["candsize"] - refsize = args["refsize"] - believer = args["believer"] - else: - candsize, refsize, believer = None, None, None - - out = data_cls.out - sim_f = data_cls.sim - - sim_specs = { - "sim_f": sim_f, - "in": ["thetas"], - "out": out, - "user": {"function": data_cls.function}, - } - - gen_out = [ - ("thetas", float, data_cls.p), - ("priority", int), - ("obs", float, (1,)), - ("obsvar", float, (1,)), - ("TV", float), - ("HD", float), - ("AE", float), - ("time", float), - ] - - gen_specs = { - "gen_f": gen_f, - "persis_in": [o[0] for o in gen_out] + ["f", "sim_id"], - "out": gen_out, - "user": { - "mini_batch": mini_batch, # No. of thetas to generate per step - "nworkers": nworkers, - "AL": AL_type, - "seed_n0": seed_n0, - "synth_cls": data_cls, - "test_data": test_data, - "prior": prior, - "candsize": candsize, - "refsize": refsize, - "believer": believer, - }, - } - - alloc_specs = { - "alloc_f": alloc_f, - "user": { - "init_sample_size": 0, - "async_return": True, # True = Return results to gen as they come in (after sample) - "active_recv_gen": True, # Persistent gen can handle irregular communications - }, - } - libE_specs = {"nworkers": nworkers, "comms": "local"} - # libE_specs = {'nworkers': nworkers, 'comms': 'local', 'sim_dirs_make': True, - # 'sim_dir_copy_files': [os.path.join(os.getcwd(), '48Ca_template.in')]} - - persis_info = add_unique_random_streams({}, nworkers + 1) - - # Currently just allow gen to exit if mse goes below threshold value - exit_criteria = {"sim_max": max_evals} # Now just a set number of sims. - - # Perform the run - H, persis_info, flag = libE( - sim_specs, - gen_specs, - exit_criteria, - persis_info, - alloc_specs=alloc_specs, - libE_specs=libE_specs, - ) - - emu = fit_emulator(data_cls.x, H["thetas"], H["f"][None, :], data_cls.thetalimits) - - TV, HD, AE = collect_data( - emu, - data_cls.x, - H["f"][None, :], - data_cls.x, - test_data["theta"], - test_data["p_prior"], - test_data["p"], - np.reshape(data_cls.real_data[0, :], (1, data_cls.real_data.shape[1])), - data_cls.obsvar.reshape(1, data_cls.d, data_cls.d), - ) - - fitinfo["f"] = H["f"] - fitinfo["theta"] = H["thetas"] - fitinfo["TV"] = np.concatenate((H["TV"], np.array([TV]))) - fitinfo["HD"] = np.concatenate((H["HD"], np.array([HD]))) - fitinfo["AE"] = np.concatenate((H["AE"], np.array([AE]))) - - fitinfo["time"] = H["time"] - return - - -def compute_timepass(start_time, new_theta): - end_time = time.time() - timepass = end_time - start_time - timepassvec = np.zeros(new_theta.shape[0]) - timepassvec[0] = timepass - return timepassvec - - -def collect_data( - emu, x, fevals, real_x, thetatest, priortest, posttest, true_fevals, obsvar3d -): - - emupredict = emu.predict(x=x, theta=thetatest) - emumean = emupredict.mean() - emuvar, is_cov = get_emuvar(emupredict) - emumeanT = emumean.T - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - - posttesthat = multiple_pdfs( - true_fevals, emumeanT[:, real_x.flatten()], var_obsvar1[:, real_x, real_x.T] - ) - - TV = np.mean(np.abs(posttest - posttesthat * priortest)) - HD = np.sqrt(0.5 * np.mean((np.sqrt(posttesthat) - np.sqrt(posttest)) ** 2)) - idnan = np.isnan(fevals).any(axis=0).flatten() - fevals_c = fevals[:, ~idnan] - AE = np.min(np.sum(np.abs(true_fevals - fevals_c.T), axis=1)) - - return TV, HD, AE - - -def gen_f(H, persis_info, gen_specs, libE_info): - """Generator to select and obviate parameters for calibration.""" - ps = PersistentSupport(libE_info, EVAL_GEN_TAG) - rand_stream = persis_info["rand_stream"] - mini_batch = gen_specs["user"]["mini_batch"] - n_workers = gen_specs["user"]["nworkers"] - AL = gen_specs["user"]["AL"] - seed = gen_specs["user"]["seed_n0"] - synth_info = gen_specs["user"]["synth_cls"] - test_data = gen_specs["user"]["test_data"] - prior_func = gen_specs["user"]["prior"] - candsize = gen_specs["user"]["candsize"] - refsize = gen_specs["user"]["refsize"] - believer = gen_specs["user"]["believer"] - - obsvar = synth_info.obsvar - data = synth_info.real_data - theta_limits = synth_info.thetalimits - - thetatest, posttest, ftest, priortest = None, None, None, None - if test_data is not None: - thetatest, posttest, ftest, priortest, thetainit, finit = ( - test_data["theta"], - test_data["p"], - test_data["f"], - test_data["p_prior"], - test_data["thetainit"], - test_data["finit"], - ) - - true_fevals = np.reshape(data[0, :], (1, data.shape[1])) - n_x, x, real_x = synth_info.d, synth_info.x, synth_info.real_x - obsvar3d = obsvar.reshape(1, n_x, n_x) - obs_offset, theta_offset, generated_no = 0, 0, 0 - TV, HD, AE, time_pass = 1000, 1000, 1000, 0 - fevals, pending, prev_pending, complete, prev_complete = ( - None, - None, - None, - None, - None, - ) - first_iter = True - tag = 0 - update_model = False - acquisition_f = eval(AL) - list_id = [] - theta = 0 - - while tag not in [STOP_TAG, PERSIS_STOP]: - starttime = time.time() - if not first_iter: - # Update fevals from calc_in - update_arrays( - n_x, - fevals, - pending, - complete, - calc_in, - obs_offset, - theta_offset, - list_id, - ) - update_model = rebuild_condition_opt( - complete, prev_complete, n_theta=mini_batch - ) - - if not update_model: - tag, Work, calc_in = ps.recv() - if tag in [STOP_TAG, PERSIS_STOP]: - break - - if update_model: - starttime = time.time() - - # if len(theta) % 50 == 0: - # print("Updating model...\n") - - # print( - # "Percentage Pending: %0.2f ( %d / %d)" - # % ( - # 100 * np.round(np.mean(pending), 4), - # np.sum(pending), - # np.prod(pending.shape), - # ) - # ) - # print( - # "Percentage Complete: %0.2f ( %d / %d)" - # % ( - # 100 * np.round(np.mean(complete), 4), - # np.sum(complete), - # np.prod(pending.shape), - # ) - # ) - - fcomb = np.concatenate((finit, fevals), axis=1) - thetacomb = np.concatenate((thetainit, theta), axis=0) - emu = fit_emulator(x, thetacomb, fcomb, theta_limits) - prev_pending = pending.copy() - update_model = False - - # Obtain the accuracy on the test set - if test_data is not None: - TV, HD, AE = collect_data( - emu, - x, - fcomb, - real_x, - thetatest, - priortest, - posttest, - true_fevals, - obsvar3d, - ) - - if first_iter: - emuinit = fit_emulator(x, thetainit, finit, theta_limits) - TV, HD, AE = collect_data( - emuinit, - x, - finit, - real_x, - thetatest, - priortest, - posttest, - true_fevals, - obsvar3d, - ) - n_init = n_workers - 1 - - theta = acquisition_f( - n_init, - x, - real_x, - emuinit, - thetainit, - finit, - true_fevals, - obsvar, - theta_limits, - prior_func, - thetatest, - priortest, - None, - believer=believer, - candsize=candsize, - refsize=refsize, - ) - - fevals, pending, prev_pending, complete, prev_complete = create_arrays( - n_x, n_init - ) - time_pass = compute_timepass(starttime, theta) - H_o = np.zeros(len(theta), dtype=gen_specs["out"]) - H_o = load_H_opt( - H_o, theta, TV, HD, AE, time_pass, generated_no, set_priorities=True - ) - tag, Work, calc_in = ps.send_recv(H_o) - first_iter = False - generated_no += n_init - - else: - if rebuild_condition_opt(complete, prev_complete, n_theta=mini_batch): - - prev_complete = complete.copy() - new_theta = acquisition_f( - mini_batch, - x, - real_x, - emu, - thetacomb, - fcomb, - true_fevals, - obsvar, - theta_limits, - prior_func, - thetatest, - priortest, - None, - believer=believer, - candsize=candsize, - refsize=refsize, - ) - - theta, fevals, pending, prev_pending, complete, prev_complete = ( - pad_arrays( - n_x, - new_theta, - theta, - fevals, - pending, - prev_pending, - complete, - prev_complete, - ) - ) - - time_pass = compute_timepass(starttime, new_theta) - - H_o = np.zeros(len(new_theta), dtype=gen_specs["out"]) - H_o = load_H_opt( - H_o, - new_theta, - TV, - HD, - AE, - time_pass, - generated_no, - set_priorities=True, - ) - tag, Work, calc_in = ps.send_recv(H_o) - generated_no += mini_batch - - return None, persis_info, FINISHED_PERSISTENT_GEN_TAG diff --git a/PUQ/designmethods/SEQCALsupport.py b/PUQ/designmethods/SEQCALsupport.py deleted file mode 100644 index 05ffe36..0000000 --- a/PUQ/designmethods/SEQCALsupport.py +++ /dev/null @@ -1,144 +0,0 @@ -import numpy as np -from PUQ.surrogate import emulator -import pyximport - -pyximport.install(setup_args={"include_dirs": np.get_include()}, reload_support=True) - - -def select_condition(complete, prev_complete, n_theta=2, n_initial=10): - # return False if np.sum(complete) - np.sum(prev_complete) < n_theta else True - if (np.sum(complete) - np.sum(prev_complete) < n_theta) or ( - np.sum(complete) < n_initial - ): - nflag = False - else: - nflag = True - return nflag - - -def rebuild_condition(complete, prev_complete, n_theta=2, n_initial=10): - if (np.sum(complete) - np.sum(prev_complete) < n_theta) or ( - np.sum(complete) < n_initial - ): - nflag = False - else: - nflag = True - return nflag - - -def rebuild_condition_opt(complete, prev_complete, n_theta=2): - - if np.sum(complete) - np.sum(prev_complete) < n_theta: - nflag = False - else: - nflag = True - return nflag - - -def create_arrays(n_x, n_thetas): - """Create 2D (point * rows) arrays fevals, pending and complete""" - - fevals = np.full((n_x, n_thetas), np.nan) - pending = np.full((1, n_thetas), True) - prev_pending = pending.copy() - complete = np.full((1, n_thetas), False) - prev_complete = np.full((1, n_thetas), False) - - return fevals, pending, prev_pending, complete, prev_complete - - -def pad_arrays( - n_x, thetanew, theta, fevals, pending, prev_pending, complete, prev_complete -): - """Extend arrays to appropriate sizes.""" - n_thetanew = len(thetanew) - theta = np.vstack((theta, thetanew)) - fevals = np.hstack((fevals, np.full((n_x, n_thetanew), np.nan))) - pending = np.hstack((pending, np.full((1, n_thetanew), True))) - prev_pending = np.hstack((prev_pending, np.full((1, n_thetanew), True))) - complete = np.hstack((complete, np.full((1, n_thetanew), False))) - prev_complete = np.hstack((prev_complete, np.full((1, n_thetanew), False))) - - return theta, fevals, pending, prev_pending, complete, prev_complete - - -def update_arrays( - n_x, fevals, pending, complete, calc_in, obs_offset, theta_offset, list_id -): - """Unpack from calc_in into 2D (point * rows) fevals""" - - sim_id = calc_in["sim_id"] - list_id.append(sim_id) - r = np.repeat(0, len(sim_id - obs_offset)) - c = sim_id - obs_offset - - if n_x < 2: - fevals[r, c + theta_offset] = calc_in["f"] - else: - rc = [i for j in range(len(sim_id - obs_offset)) for i in range(n_x)] - cc = np.repeat(c, n_x) - fevals[rc, cc] = calc_in["f"].flatten() - - pending[r, c + theta_offset] = False - complete[r, c + theta_offset] = True - return - - -def assign_priority(n_thetas, generated_no): - """Assign priorities to points.""" - priority = np.arange(generated_no, generated_no + n_thetas) - return priority - - -def load_H(H, thetas, mse, hd, generated_no, offset=0, set_priorities=False): - """Fill inputs into H0. - There will be num_points x num_thetas entries - """ - n_thetas = len(thetas) - start = offset * n_thetas - - if thetas.shape[1] < 2: - H["thetas"][start : start + n_thetas] = thetas.flatten() - else: - H["thetas"][start : start + n_thetas] = thetas - - H["TV"][start : start + n_thetas] = np.repeat(mse, n_thetas) - H["HD"][start : start + n_thetas] = np.repeat(hd, n_thetas) - if set_priorities: - H["priority"] = assign_priority(n_thetas, generated_no) - - return H - - -def load_H_opt( - H, thetas, mse, hd, ae, time, generated_no, offset=0, set_priorities=False -): - """Fill inputs into H0. - There will be num_points x num_thetas entries - """ - n_thetas = len(thetas) - start = offset * n_thetas - - if thetas.shape[1] < 2: - H["thetas"][start : start + n_thetas] = thetas.flatten() - else: - H["thetas"][start : start + n_thetas] = thetas - - H["TV"][start : start + n_thetas] = np.repeat(mse, n_thetas) - H["HD"][start : start + n_thetas] = np.repeat(hd, n_thetas) - H["AE"][start : start + n_thetas] = np.repeat(ae, n_thetas) - H["time"][start : start + n_thetas] = time - if set_priorities: - H["priority"] = assign_priority(n_thetas, generated_no) - - return H - - -def fit_emulator(x, theta, fevals, thetalimits): - idnan = np.isnan(fevals).any(axis=0).flatten() - fevals_c = fevals[:, ~idnan] - theta_c = theta[~idnan, :] - - emu = emulator(x, theta_c, fevals_c, method="PCGP") - - return emu diff --git a/PUQ/designmethods/SEQDES.py b/PUQ/designmethods/SEQDES.py deleted file mode 100644 index f5cde87..0000000 --- a/PUQ/designmethods/SEQDES.py +++ /dev/null @@ -1,307 +0,0 @@ -import numpy as np -from PUQ.designmethods.gen_funcs.CIMSPE import imspe -from PUQ.designmethods.gen_funcs.CMAXVAR import maxvar -from PUQ.designmethods.gen_funcs.CEIVAR import ceivar -from PUQ.designmethods.gen_funcs.CEIVARX import ceivarx -from PUQ.designmethods.SEQCALsupport import ( - load_H, - update_arrays, - create_arrays, - pad_arrays, - select_condition, - rebuild_condition, -) -from PUQ.designmethods.SEQDESsupport import collect_data, fit_emulator1d, find_mle -from libensemble.message_numbers import ( - STOP_TAG, - PERSIS_STOP, - FINISHED_PERSISTENT_GEN_TAG, - EVAL_GEN_TAG, -) -from libensemble.tools.persistent_support import PersistentSupport -from libensemble.alloc_funcs.start_only_persistent import ( - only_persistent_gens as alloc_f, -) -from libensemble.libE import libE -from libensemble.tools import parse_args, save_libE_output, add_unique_random_streams - - -def fit(fitinfo, data_cls, args): - - mini_batch = args["mini_batch"] - n_init_thetas = args["n_init_thetas"] - nworkers = args["nworkers"] - AL_type = args["AL"] - seed_n0 = args["seed_n0"] - prior = args["prior"] - max_evals = args["max_evals"] - test_data = args["data_test"] - theta_torun = args["theta_torun"] - is_thetamle = args["is_thetamle"] - - out = data_cls.out - sim_f = data_cls.sim - - sim_specs = { - "sim_f": sim_f, - "in": ["thetas"], - "out": out, - "user": {"function": data_cls.function}, - } - - gen_out = [ - ("thetas", float, data_cls.p), - ("priority", int), - ("obs", float, (1,)), - ("obsvar", float, (1,)), - ("TV", float), - ("HD", float), - ("thetamle", float, (1,)), - ] - - gen_specs = { - "gen_f": gen_f, - "persis_in": [o[0] for o in gen_out] + ["f", "sim_id"], - "out": gen_out, - "user": { - "n_init_thetas": n_init_thetas, # Num thetas in initial batch - "mini_batch": mini_batch, # No. of thetas to generate per step - "nworkers": nworkers, - "AL": AL_type, - "seed_n0": seed_n0, - "synth_cls": data_cls, - "test_data": test_data, - "prior": prior, - "theta_torun": theta_torun, - "is_thetamle": is_thetamle, - }, - } - - alloc_specs = { - "alloc_f": alloc_f, - "user": { - "init_sample_size": 0, - "async_return": True, # True = Return results to gen as they come in (after sample) - "active_recv_gen": True, # Persistent gen can handle irregular communications - }, - } - libE_specs = {"nworkers": nworkers, "comms": "local"} - # libE_specs = {'nworkers': nworkers, 'comms': 'local', 'sim_dirs_make': True, - # 'sim_dir_copy_files': [os.path.join(os.getcwd(), '48Ca_template.in')]} - - persis_info = add_unique_random_streams({}, nworkers + 1) - - # Currently just allow gen to exit if mse goes below threshold value - exit_criteria = {"sim_max": max_evals} # Now just a set number of sims. - - # Perform the run - H, persis_info, flag = libE( - sim_specs, - gen_specs, - exit_criteria, - persis_info, - alloc_specs=alloc_specs, - libE_specs=libE_specs, - ) - - fitinfo["f"] = H["f"] - fitinfo["theta"] = H["thetas"] - fitinfo["TV"] = H["TV"] - fitinfo["HD"] = H["HD"] - - for key in persis_info.keys(): - # print(type(persis_info[key])) - if isinstance(persis_info[key], dict): - if "thetamle" in persis_info[key].keys(): - fitinfo["thetamle"] = persis_info[key]["thetamle"] - - return - - -def gen_f(H, persis_info, gen_specs, libE_info): - """Generator to select and obviate parameters for calibration.""" - ps = PersistentSupport(libE_info, EVAL_GEN_TAG) - rand_stream = persis_info["rand_stream"] - n0 = gen_specs["user"]["n_init_thetas"] - mini_batch = gen_specs["user"]["mini_batch"] - n_workers = gen_specs["user"]["nworkers"] - AL = gen_specs["user"]["AL"] - seed = gen_specs["user"]["seed_n0"] - theta_torun = gen_specs["user"]["theta_torun"] - is_thetamle = gen_specs["user"]["is_thetamle"] - # Prior functions - prior_func_all = gen_specs["user"]["prior"] - prior_func, prior_func_x, prior_func_t = ( - prior_func_all["prior"], - prior_func_all["priorx"], - prior_func_all["priort"], - ) - - # Simulation info - synth_info = gen_specs["user"]["synth_cls"] - obsvar, data, theta_limits, dim, x = ( - synth_info.obsvar, - synth_info.real_data, - synth_info.thetalimits, - synth_info.d, - synth_info.x, - ) - - # Test data - test_data = gen_specs["user"]["test_data"] - thetatest, posttest, ftest, priortest = None, None, None, None - if test_data is not None: - thetatest, th_mesh, x_mesh, ptest, ftest, priortest, ytest = ( - test_data["theta"], - test_data["th"], - test_data["xmesh"], - test_data["p"], - test_data["f"], - test_data["p_prior"], - test_data["y"], - ) - - # Additional set - true_fevals = np.reshape(data[0, :], (1, data.shape[1])) - x_emu = np.arange(0, 1)[:, None] - dx, dt, nmesh = x.shape[1], th_mesh.shape[1], len(x_mesh) - obs_offset, theta_offset, generated_no = 0, 0, 0 - TV, HD, tag, theta = 1000, 1000, 0, 0 - fevals, pending, prev_pending, complete, prev_complete = ( - None, - None, - None, - None, - None, - ) - first_iter, update_model = True, False - list_id, mlelist = [], [] - - if AL == None: - pass - else: - acquisition_f = eval(AL) - - while tag not in [STOP_TAG, PERSIS_STOP]: - if not first_iter: - # Update fevals from calc_in - update_arrays( - dim, - fevals, - pending, - complete, - calc_in, - obs_offset, - theta_offset, - list_id, - ) - update_model = rebuild_condition( - complete, prev_complete, n_theta=mini_batch, n_initial=n0 - ) - - if not update_model: - tag, Work, calc_in = ps.recv() - if tag in [STOP_TAG, PERSIS_STOP]: - break - - if update_model: - - emu = fit_emulator1d(x_emu, theta, fevals) - if is_thetamle: - theta_mle = np.array(synth_info.true_theta)[None, :] - else: - theta_mle = find_mle( - emu, x, x_emu, true_fevals, obsvar, dx, dt, theta_limits, False - ) - - mlelist.append(theta_mle) - - if len(theta) % 10 == 0: - print("mle:", theta_mle) - - TV, HD = collect_data( - emu, - None, - x_emu, - theta_mle, - dt, - x_mesh, - thetatest, - nmesh, - ytest, - ptest, - x, - true_fevals, - obsvar, - synth_info, - ) - prev_pending = pending.copy() - update_model = False - - if first_iter: - n_init = max(n_workers - 1, n0) - theta = prior_func.rnd(n_init, seed) - fevals, pending, prev_pending, complete, prev_complete = create_arrays( - dim, n_init - ) - - H_o = np.zeros(len(theta), dtype=gen_specs["out"]) - H_o = load_H(H_o, theta, TV, HD, generated_no, set_priorities=True) - tag, Work, calc_in = ps.send_recv(H_o) - first_iter = False - generated_no += n_init - - else: - if select_condition( - complete, prev_complete, n_theta=mini_batch, n_initial=n0 - ): - - prev_complete = complete.copy() - - if AL == None: - new_theta = theta_torun[ - (generated_no - n_init) : (generated_no - n_init + mini_batch), - :, - ] - else: - new_theta = acquisition_f( - mini_batch, - x, - None, - emu, - theta, - fevals, - true_fevals, - obsvar, - theta_limits, - prior_func, - prior_func_t, - thetatest, - x_mesh, - th_mesh, - priortest, - None, - synth_info, - theta_mle, - ) - - theta, fevals, pending, prev_pending, complete, prev_complete = ( - pad_arrays( - dim, - new_theta, - theta, - fevals, - pending, - prev_pending, - complete, - prev_complete, - ) - ) - - H_o = np.zeros(len(new_theta), dtype=gen_specs["out"]) - H_o = load_H(H_o, new_theta, TV, HD, generated_no, set_priorities=True) - tag, Work, calc_in = ps.send_recv(H_o) - generated_no += mini_batch - - persis_info["thetamle"] = mlelist - return None, persis_info, FINISHED_PERSISTENT_GEN_TAG diff --git a/PUQ/designmethods/SEQDESsupport.py b/PUQ/designmethods/SEQDESsupport.py deleted file mode 100644 index 0d96e7f..0000000 --- a/PUQ/designmethods/SEQDESsupport.py +++ /dev/null @@ -1,226 +0,0 @@ -import numpy as np -from PUQ.surrogate import emulator -import pyximport -import scipy.optimize as spo -from sklearn.linear_model import LinearRegression -from PUQ.surrogatemethods.PCGPexp import postpred, postpredbias - -pyximport.install(setup_args={"include_dirs": np.get_include()}, reload_support=True) - - -def fit_emulator1d(x_emu, theta, fevals): - - emu = emulator(x_emu, theta, fevals, method="PCGPexp") - - return emu - - -def compute_diff(x, parameter, nx, emu, x_emu, obs, is_bias): - # obs : 1 x n_x - # emumean : 1 x n_x - # bias : n_x x 1 - dt = len(parameter) - dx = x.shape[1] - xp = [ - np.concatenate([xc.reshape(1, dx), parameter.reshape(1, dt)], axis=1) - for xc in x - ] - xp = np.array([m for mesh in xp for m in mesh]) - # xp = np.concatenate((x, np.repeat(parameter, nx).reshape(nx, len(parameter))), axis=1) - - # Predict computer model - emupred = emu.predict(x=x_emu, theta=xp) - emumean = emupred.mean() - - # Predict linear bias mean - bias = (obs - emumean).T - - if is_bias: - model = LinearRegression().fit(x, bias) - mu_bias = model.predict(x) - diff = bias - mu_bias - return diff - else: - return bias - - -def obj_mle(parameter, args): - emu, x, x_emu, obs, obsvar, is_bias = ( - args[0], - args[1], - args[2], - args[3], - args[4], - args[5], - ) - nx = len(x) - diff = compute_diff(x, parameter, nx, emu, x_emu, obs, is_bias) - ll = diff.T @ diff - return ll.flatten() - - -def find_mle(emu, x, x_emu, obs, obsvar, dx, dt, theta_limits, is_bias): - - bnd = () - theta_init = [] - for i in range(dx, dx + dt): - bnd += ((theta_limits[i][0], theta_limits[i][1]),) - theta_init.append((theta_limits[i][0] + theta_limits[i][1]) / 2) - - opval = spo.minimize( - obj_mle, - theta_init, - method="L-BFGS-B", - options={"gtol": 0.01}, - bounds=bnd, - args=([emu, x, x_emu, obs, obsvar, is_bias]), - ) - - theta_mle = opval.x - theta_mle = theta_mle.reshape(1, dt) - return theta_mle - - -def collect_data( - emu, - emubias, - x_emu, - theta_mle, - dt, - xmesh, - xtmesh, - nmesh, - ytest, - ptest, - x, - obs, - obsvar, - synth_info, -): - - dx = xmesh.shape[1] - xtrue_test = [ - np.concatenate([xc.reshape(1, dx), theta_mle], axis=1) for xc in xmesh - ] - xtrue_test = np.array([m for mesh in xtrue_test for m in mesh]) - predobj = emu.predict(x=x_emu, theta=xtrue_test) - - fmeanhat, fvarhat = predobj.mean(), predobj.var() - - if emubias == None: - pred_error = np.mean(np.abs(fmeanhat - ytest)) - pmeanhat, pvarhat = postpred(emu._info, x, xtmesh, obs, obsvar) - post_error = np.mean(np.abs(pmeanhat - ptest)) - else: - bmeanhat = emubias.predict(xmesh) - pred_error = np.mean(np.abs(fmeanhat + bmeanhat - ytest)) - bmeanhat = emubias.predict(x) - pmeanhat, pvarhat = postpredbias(emu._info, x, xtmesh, obs, obsvar, bmeanhat) - post_error = np.mean(np.abs(pmeanhat - ptest)) - - return pred_error, post_error - - -def find_abs_diff(nx, d, x): - # diff = np.zeros((nx, nx)) - # for i in range(nx): - # for j in range(nx): - # for k in range(d): - # diff[i, j] += np.abs(x[i, k] - x[j, k]) - diff = np.sum(np.abs(x[:, None, :] - x[None, :, :]), axis=-1) - - return diff - - -def gen_cov(sigmae_sq, sigmab_sq, lambdap, nx, abs_dist): - return np.diag(np.repeat(sigmae_sq, nx)) + sigmab_sq * np.exp(-lambdap * abs_dist) - - -def obj_covmle(parameter, args): - - x, biasdiff = args[0], args[1] - nx, d = x.shape[0], x.shape[1] - - abs_dist = find_abs_diff(nx, d, x) - # Generate cov - covmat = gen_cov( - sigmae_sq=parameter[0], - sigmab_sq=parameter[1], - lambdap=parameter[2], - nx=nx, - abs_dist=abs_dist, - ) - - # Inverse of covmat - covmatinv = np.linalg.inv(covmat) - - # Negative likelihood - ll = np.log(np.linalg.det(covmat)) + biasdiff @ covmatinv @ biasdiff.T - - return ll.flatten() - - -def find_covparam(x, biasdiff): - bnd = () - theta_init = [] - limits = [0.0001, 0.5] - # limits = [0.0001, 50] - for i in range(0, 3): - bnd += ((limits[0], limits[1]),) - theta_init.append((limits[1]) / 2) - - opval = spo.minimize( - obj_covmle, - theta_init, - method="L-BFGS-B", - options={"gtol": 0.01}, - bounds=bnd, - args=([x, biasdiff]), - ) - - return opval.x[0], opval.x[1], opval.x[2] - - -def bias_predict(emu, theta_mle, x_emu, x, obs, unknowncov=False): - - nx = len(x) - dx = x.shape[1] - xp = [np.concatenate([xc.reshape(1, dx), theta_mle], axis=1) for xc in x] - xp = np.array([m for mesh in xp for m in mesh]) - - # Predict computer model - emupred = emu.predict(x=x_emu, theta=xp) - emumean = emupred.mean() - - # Predict linear bias mean - bias = (obs - emumean).T - model = LinearRegression().fit(x, bias) - mu_bias = model.predict(x) - diff = bias - mu_bias - - if unknowncov: - sigmae_sq, sigmab_sq, lambdap = find_covparam(x, diff.T) - - class biaspred: - def __init__(self, model): - self.model = model - - def predict(self, xnew): - return self.model.predict(xnew).T - - if unknowncov: - - def predictcov(self, xnew): - nx, d = xnew.shape[0], xnew.shape[1] - abs_dist = find_abs_diff(nx, d, xnew) - covmat = gen_cov( - sigmae_sq=sigmae_sq, - sigmab_sq=sigmab_sq, - lambdap=lambdap, - nx=nx, - abs_dist=abs_dist, - ) - return covmat - - biasobj = biaspred(model) - return biasobj diff --git a/PUQ/designmethods/SEQUNIFORM.py b/PUQ/designmethods/SEQUNIFORM.py deleted file mode 100644 index 2b9da27..0000000 --- a/PUQ/designmethods/SEQUNIFORM.py +++ /dev/null @@ -1,208 +0,0 @@ -import numpy as np -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - get_emuvar, - multiple_pdfs, -) -from PUQ.designmethods.gen_funcs.acquisition_funcs import maxvar, eivar, maxexp, rnd -from PUQ.designmethods.SEQCALsupport import ( - fit_emulator, - load_H, - update_arrays, - create_arrays, - pad_arrays, - select_condition, - rebuild_condition, -) -from libensemble.message_numbers import ( - STOP_TAG, - PERSIS_STOP, - FINISHED_PERSISTENT_GEN_TAG, - EVAL_GEN_TAG, -) -from libensemble.tools.persistent_support import PersistentSupport -from libensemble.alloc_funcs.start_only_persistent import ( - only_persistent_gens as alloc_f, -) -from libensemble.libE import libE -from libensemble.tools import parse_args, save_libE_output, add_unique_random_streams -from PUQ.prior import prior_dist - - -def fit(fitinfo, data_cls, args): - mini_batch = args["mini_batch"] - n_init_thetas = args["n_init_thetas"] - nworkers = args["nworkers"] - max_evals = args["max_evals"] - - out = data_cls.out - sim_f = data_cls.sim - - sim_specs = { - "sim_f": sim_f, - "in": ["thetas"], - "out": out, - "user": {"function": data_cls.function}, - } - - gen_out = [ - ("thetas", float, data_cls.p), - ("priority", int), - ("obs", float, (1,)), - ("obsvar", float, (1,)), - ("TV", float), - ("HD", float), - ] - - gen_specs = { - "gen_f": gen_f, - "persis_in": [o[0] for o in gen_out] + ["f", "sim_id"], - "out": gen_out, - "user": { - "n_init_thetas": n_init_thetas, # Num thetas in initial batch - "mini_batch": mini_batch, # No. of thetas to generate per step - "nworkers": nworkers, - "synth_cls": data_cls, - }, - } - - alloc_specs = { - "alloc_f": alloc_f, - "user": { - "init_sample_size": 0, - "async_return": True, # True = Return results to gen as they come in (after sample) - "active_recv_gen": True, # Persistent gen can handle irregular communications - }, - } - libE_specs = {"nworkers": nworkers, "comms": "local"} - - persis_info = add_unique_random_streams({}, nworkers + 1) - - # Currently just allow gen to exit if mse goes below threshold value - exit_criteria = {"sim_max": max_evals} # Now just a set number of sims. - - # Perform the run - H, persis_info, flag = libE( - sim_specs, - gen_specs, - exit_criteria, - persis_info, - alloc_specs=alloc_specs, - libE_specs=libE_specs, - ) - - fitinfo["f"] = H["f"] - fitinfo["theta"] = H["thetas"] - fitinfo["TV"] = H["TV"] - fitinfo["HD"] = H["HD"] - return - - -def gen_f(H, persis_info, gen_specs, libE_info): - """Generator to select and obviate parameters for calibration.""" - ps = PersistentSupport(libE_info, EVAL_GEN_TAG) - rand_stream = persis_info["rand_stream"] - n0 = gen_specs["user"]["n_init_thetas"] - mini_batch = gen_specs["user"]["mini_batch"] - n_workers = gen_specs["user"]["nworkers"] - synth_info = gen_specs["user"]["synth_cls"] - - theta_torun = synth_info.theta - obsvar = synth_info.obsvar - data = synth_info.real_data - theta_limits = synth_info.thetalimits - - true_fevals = np.reshape(data[0, :], (1, data.shape[1])) - - n_x = synth_info.d - n_realx = true_fevals.shape[1] - x = synth_info.x - real_x = synth_info.real_x - - obs_offset, theta_offset, generated_no = 0, 0, 0 - TV, HD = 1000, 1000 - fevals, pending, prev_pending, complete, prev_complete = ( - None, - None, - None, - None, - None, - ) - first_iter = True - tag = 0 - - obsvar3d = obsvar.reshape(1, n_x, n_x) - update_model = False - list_id = [] - - theta = 0 - - while tag not in [STOP_TAG, PERSIS_STOP]: - if not first_iter: - # Update fevals from calc_in - update_arrays( - n_x, - fevals, - pending, - complete, - calc_in, - obs_offset, - theta_offset, - list_id, - ) - update_model = rebuild_condition( - complete, prev_complete, n_theta=mini_batch, n_initial=n0 - ) - - if not update_model: - tag, Work, calc_in = ps.recv() - if tag in [STOP_TAG, PERSIS_STOP]: - break - - if first_iter: - # print('Selecting theta for the first iteration...\n') - - n_init = max(n_workers - 1, n0) - theta = theta_torun[0:n_init, :] - fevals, pending, prev_pending, complete, prev_complete = create_arrays( - n_x, n_init - ) - - H_o = np.zeros(len(theta), dtype=gen_specs["out"]) - H_o = load_H(H_o, theta, TV, HD, generated_no, set_priorities=True) - tag, Work, calc_in = ps.send_recv(H_o) - first_iter = False - generated_no += n_init - - else: - if select_condition( - complete, prev_complete, n_theta=mini_batch, n_initial=n0 - ): - # print('Selecting theta...\n') - - prev_complete = complete.copy() - new_theta = theta_torun[generated_no : (generated_no + mini_batch), :] - - ( - theta, - fevals, - pending, - prev_pending, - complete, - prev_complete, - ) = pad_arrays( - n_x, - new_theta, - theta, - fevals, - pending, - prev_pending, - complete, - prev_complete, - ) - - H_o = np.zeros(len(new_theta), dtype=gen_specs["out"]) - H_o = load_H(H_o, new_theta, TV, HD, generated_no, set_priorities=True) - tag, Work, calc_in = ps.send_recv(H_o) - generated_no += mini_batch - - return None, persis_info, FINISHED_PERSISTENT_GEN_TAG diff --git a/PUQ/designmethods/gen_funcs/CEIVAR.py b/PUQ/designmethods/gen_funcs/CEIVAR.py deleted file mode 100644 index adf8759..0000000 --- a/PUQ/designmethods/gen_funcs/CEIVAR.py +++ /dev/null @@ -1,176 +0,0 @@ -import numpy as np -from PUQ.surrogatemethods.PCGPexp import temp_postphimat, postphimat -from smt.sampling_methods import LHS -from numpy.random import rand -import scipy.stats as sps - - -def ceivar( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - prior_func_t, - thetatest=None, - xmesh=None, - thetamesh=None, - posttest=None, - type_init=None, - synth_info=None, - theta_mle=None, -): - """ - Smat3D : ncand x nfield x nfield (3D) Emulator Covariance matrix - pred_mean : ncand x nfield (2D) Emulator Mean matrix - rVh_1_3d : ncand x nfield x nacquired (3D) matrix - """ - - p = theta.shape[1] - dt = thetamesh.shape[1] - dx = x.shape[1] - n_x = x.shape[0] - - # REMOVE THESE LINES - # sampling = LHS(xlimits=thetalimits[dx:, :]) - # thetamesh = sampling(synth_info.meshsize) - # print('mesh size') - # print(thetamesh.shape) - - xuniq = np.unique(x, axis=0) - if synth_info.data_name == "covid19": - clist = construct_candlist_covid(thetalimits, xuniq, prior_func, prior_func_t) - elif synth_info.data_name == "highdim": - clist = construct_candlist_high(thetalimits, xuniq, prior_func, prior_func_t) - else: - clist = construct_candlist(thetalimits, xuniq, prior_func, prior_func_t) - - xt_ref = np.array([np.concatenate([xc, th]) for th in thetamesh for xc in x]) - Smat3D, rVh_1_3d, pred_mean = temp_postphimat(emu._info, n_x, xt_ref, obs, obsvar) - - eivar_val = np.zeros(len(clist)) - for xt_id, xt_c in enumerate(clist): - eivar_val[xt_id] = postphimat( - emu._info, - n_x, - xt_ref, - obs, - obsvar, - xt_c.reshape(1, p), - Smat3D, - rVh_1_3d, - pred_mean, - ) - - th_cand = clist[np.argmax(eivar_val), :].reshape(1, p) - - return th_cand - - -def ceivarbias( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - prior_func_t, - thetatest=None, - xmesh=None, - thetamesh=None, - posttest=None, - emubias=None, - synth_info=None, - theta_mle=None, - unknowncov=None, -): - - p = theta.shape[1] - dt = thetamesh.shape[1] - dx = x.shape[1] - n_x = x.shape[0] - x_emu = np.arange(0, 1)[:, None] - - bias_mean = emubias.predict(x) - if unknowncov: - bias_var = emubias.predictcov(x) - else: - bias_var = 1 * obsvar - - xuniq = np.unique(x, axis=0) - # Create a candidate list - if synth_info.data_name == "covid19": - clist = construct_candlist_covid(thetalimits, xuniq, prior_func, prior_func_t) - else: - clist = construct_candlist(thetalimits, xuniq, prior_func, prior_func_t) - - thetatest = np.array([np.concatenate([xc, th]) for th in thetamesh for xc in x]) - - Smat3D, rVh_1_3d, pred_mean = temp_postphimat( - emu._info, n_x, thetatest, obs, bias_var - ) - eivar_val = np.zeros(len(clist)) - - for xt_id, xt_c in enumerate(clist): - eivar_val[xt_id] = postphimat( - emu._info, - n_x, - thetatest, - obs - bias_mean, - bias_var, - xt_c.reshape(1, p), - Smat3D, - rVh_1_3d, - pred_mean, - ) - th_cand = clist[np.argmax(eivar_val), :].reshape(1, p) - - return th_cand - - -def construct_candlist(thetalimits, xuniq, prior_func, prior_func_t): - - n0 = 100 - n_clist = n0 * len(xuniq) - t_unif = prior_func_t.rnd(n0, None) - clist1 = np.array([np.concatenate([xc, th]) for th in t_unif for xc in xuniq]) - clist2 = prior_func.rnd(n_clist, None) - clist = np.concatenate((clist1, clist2), axis=0) - return clist - - -def construct_candlist_high(thetalimits, xuniq, prior_func, prior_func_t): - d_x = xuniq.shape[1] - sampling = LHS(xlimits=thetalimits[d_x:, :]) - t_unif = sampling(500) - clist1 = np.array([np.concatenate([xc, th]) for th in t_unif for xc in xuniq]) - sampling = LHS(xlimits=thetalimits) - clist2 = sampling(1000) - clist = np.concatenate((clist1, clist2), axis=0) - return clist - - -def construct_candlist_covid(thetalimits, xuniq, prior_func, prior_func_t): - - # 1000 = 100 x nx - n0 = 100 - nx = len(xuniq) - t_unif = prior_func_t.rnd(n0, None) - clist1 = np.array([np.concatenate([xc, th]) for th in t_unif for xc in xuniq]) - - # 1000 = 100 x nx - xref_sample = np.random.choice(a=189, size=nx, replace=False)[:, None] / 188 - t_unif = prior_func_t.rnd(n0, None) - clist2 = np.array([np.concatenate([xc, th]) for th in t_unif for xc in xref_sample]) - clist = np.concatenate((clist1, clist2), axis=0) - - return clist diff --git a/PUQ/designmethods/gen_funcs/CEIVARX.py b/PUQ/designmethods/gen_funcs/CEIVARX.py deleted file mode 100644 index a92de7f..0000000 --- a/PUQ/designmethods/gen_funcs/CEIVARX.py +++ /dev/null @@ -1,219 +0,0 @@ -import numpy as np -from PUQ.surrogatemethods.PCGPexp import temp_postphimat, postphimat -from smt.sampling_methods import LHS -from numpy.random import rand -import scipy.stats as sps - - -def ceivarx( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - prior_func_t, - thetatest=None, - x_ref=None, - theta_ref=None, - posttest=None, - type_init=None, - synth_info=None, - theta_mle=None, -): - - x_emu = np.arange(0, 1)[:, None] - xuniq = np.unique(x, axis=0) - - # Create a candidate list - if synth_info.data_name == "covid19": - clist = construct_candlist_covid(thetalimits, xuniq, prior_func, prior_func_t) - elif synth_info.data_name == "highdim": - clist = construct_candlist_high(thetalimits, xuniq, prior_func, prior_func_t) - else: - clist = construct_candlist(thetalimits, xuniq, prior_func, prior_func_t) - - dx = x_ref.shape[1] - nx_ref = x_ref.shape[0] - nt_ref = theta_ref.shape[0] - dt = theta_ref.shape[1] - nf = x.shape[0] - # Get estimate for real data at theta_mle for reference x - # nx_ref x (d_x + d_t) - xt_ref = [np.concatenate([xc.reshape(1, dx), theta_mle], axis=1) for xc in x_ref] - xt_ref = np.array([m for mesh in xt_ref for m in mesh]) - - # 1 x nx_ref - y_ref = emu.predict(x=x_emu, theta=xt_ref).mean() - # nx_ref x nf - f_temp_rep = np.repeat(obs, nx_ref, axis=0) - - # nx_ref x (nf + 1) - f_field_rep = np.concatenate((f_temp_rep, y_ref.T), axis=1) - - xs = [np.concatenate([x, xc.reshape(1, dx)], axis=0) for xc in x_ref] - ts = [np.repeat(theta_mle.reshape(1, dt), nf + 1, axis=0)] - mesh_grid = [np.concatenate([xc, th], axis=1).tolist() for xc in xs for th in ts] - mesh_grid = np.array([m for mesh in mesh_grid for m in mesh]) - - n_x = nf + 1 - - # Construct obsvar - obsvar3D = np.zeros(shape=(nx_ref, n_x, n_x)) - for i in range(nx_ref): - obsvar3D[i, :, :] = np.diag(np.repeat(synth_info.sigma2, n_x)) - - Smat3D, rVh_1_3d, pred_mean = temp_postphimat( - emu._info, n_x, mesh_grid, f_field_rep, obsvar3D - ) - eivar_val = np.zeros(len(clist)) - for xt_id, x_c in enumerate(clist): - xt_cand = x_c.reshape(1, dx + dt) - eivar_val[xt_id] = postphimat( - emu._info, - n_x, - mesh_grid, - f_field_rep, - obsvar3D, - xt_cand, - Smat3D, - rVh_1_3d, - pred_mean, - ) - - maxid = np.argmax(eivar_val) - xnew = clist[maxid].reshape(1, dx + dt) - return xnew - - -def ceivarxbias( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - prior_func_t, - thetatest=None, - x_ref=None, - theta_ref=None, - posttest=None, - emubias=None, - synth_info=None, - theta_mle=None, - unknowncov=None, -): - - x_emu = np.arange(0, 1)[:, None] - xuniq = np.unique(x, axis=0) - - # Create a candidate list - if synth_info.data_name == "covid19": - clist = construct_candlist_covid(thetalimits, xuniq, prior_func, prior_func_t) - else: - clist = construct_candlist(thetalimits, xuniq, prior_func, prior_func_t) - - nx_ref = x_ref.shape[0] - dx = x_ref.shape[1] - nt_ref = theta_ref.shape[0] - dt = theta_ref.shape[1] - nf = x.shape[0] - - # Get estimate for real data at theta_mle for reference x - # nx_ref x (d_x + d_t) - xt_ref = [np.concatenate([xc.reshape(1, dx), theta_mle], axis=1) for xc in x_ref] - xt_ref = np.array([m for mesh in xt_ref for m in mesh]) - - # 1 x nx_ref - y_ref = emu.predict(x=x_emu, theta=xt_ref).mean() - - # nx_ref x nf - bias_mean = emubias.predict(x) - f_temp_rep = np.repeat(obs - bias_mean, nx_ref, axis=0) - # nx_ref x (nf + 1) - f_field_rep = np.concatenate((f_temp_rep, (y_ref).T), axis=1) - - xs = [np.concatenate([x, xc.reshape(1, dx)], axis=0) for xc in x_ref] - ts = [np.repeat(theta_mle.reshape(1, dt), nf + 1, axis=0)] - mesh_grid = [np.concatenate([xc, th], axis=1).tolist() for xc in xs for th in ts] - mesh_grid = np.array([m for mesh in mesh_grid for m in mesh]) - - n_x = nf + 1 - - # Construct obsvar - obsvar3D = np.zeros(shape=(nx_ref, n_x, n_x)) - for i in range(nx_ref): - xnew = np.concatenate((x, x_ref[i].reshape(1, dx)), axis=0) - if unknowncov: - obsvar3D[i, :, :] = emubias.predictcov(xnew) - else: - obsvar3D[i, :, :] = np.diag(np.repeat(synth_info.sigma2, n_x)) - - Smat3D, rVh_1_3d, pred_mean = temp_postphimat( - emu._info, n_x, mesh_grid, f_field_rep, obsvar3D - ) - - eivar_val = np.zeros(len(clist)) - for xt_id, x_c in enumerate(clist): - xt_cand = x_c.reshape(1, dx + dt) - eivar_val[xt_id] = postphimat( - emu._info, - n_x, - mesh_grid, - f_field_rep, - obsvar3D, - xt_cand, - Smat3D, - rVh_1_3d, - pred_mean, - ) - - maxid = np.argmax(eivar_val) - xnew = clist[maxid].reshape(1, dx + dt) - return xnew - - -def construct_candlist_covid(thetalimits, xuniq, prior_func, prior_func_t): - - # 1000 = 100 x nx - n0 = 100 - nx = len(xuniq) - t_unif = prior_func_t.rnd(n0, None) - clist1 = np.array([np.concatenate([xc, th]) for th in t_unif for xc in xuniq]) - - # 1000 = 100 x nx - xref_sample = np.random.choice(a=189, size=nx, replace=False)[:, None] / 188 - t_unif = prior_func_t.rnd(n0, None) - clist2 = np.array([np.concatenate([xc, th]) for th in t_unif for xc in xref_sample]) - clist = np.concatenate((clist1, clist2), axis=0) - - return clist - - -def construct_candlist(thetalimits, xuniq, prior_func, prior_func_t): - n0 = 100 - n_clist = n0 * len(xuniq) - t_unif = prior_func_t.rnd(n0, None) - clist1 = np.array([np.concatenate([xc, th]) for th in t_unif for xc in xuniq]) - clist2 = prior_func.rnd(n_clist, None) - clist = np.concatenate((clist1, clist2), axis=0) - return clist - - -def construct_candlist_high(thetalimits, xuniq, prior_func, prior_func_t): - d_x = xuniq.shape[1] - sampling = LHS(xlimits=thetalimits[d_x:, :]) - t_unif = sampling(500) - clist1 = np.array([np.concatenate([xc, th]) for th in t_unif for xc in xuniq]) - sampling = LHS(xlimits=thetalimits) - clist2 = sampling(1000) - clist = np.concatenate((clist1, clist2), axis=0) - return clist diff --git a/PUQ/designmethods/gen_funcs/CIMSPE.py b/PUQ/designmethods/gen_funcs/CIMSPE.py deleted file mode 100644 index 4e75b65..0000000 --- a/PUQ/designmethods/gen_funcs/CIMSPE.py +++ /dev/null @@ -1,46 +0,0 @@ -import numpy as np -from PUQ.surrogatemethods.PCGPexp import imspe_acq -from smt.sampling_methods import LHS - - -def imspe( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - prior_func_t, - thetatest=None, - x_ref=None, - theta_ref=None, - posttest=None, - type_init=None, - synth_info=None, - theta_mle=None, -): - - # Create a candidate list - sampling = LHS(xlimits=thetalimits) - clist = sampling(1500) - - # Create grid - sampling = LHS(xlimits=thetalimits) - mesh_grid = sampling(1500) - - dx = x_ref.shape[1] - dt = theta_ref.shape[1] - - imspe_val = np.zeros(len(clist)) - for xt_id, x_c in enumerate(clist): - xt_cand = x_c.reshape(1, dx + dt) - imspe_val[xt_id] = imspe_acq(emu._info, mesh_grid, xt_cand) - - maxid = np.argmax(imspe_val) - xnew = clist[maxid].reshape(1, dx + dt) - - return xnew diff --git a/PUQ/designmethods/gen_funcs/CMAXVAR.py b/PUQ/designmethods/gen_funcs/CMAXVAR.py deleted file mode 100644 index 6d61570..0000000 --- a/PUQ/designmethods/gen_funcs/CMAXVAR.py +++ /dev/null @@ -1,56 +0,0 @@ -import numpy as np -from PUQ.surrogatemethods.PCGPexp import temp_postphimat, postphimat -from smt.sampling_methods import LHS -from numpy.random import rand -import scipy.stats as sps - - -def maxvar( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - prior_func_t, - thetatest=None, - xmesh=None, - thetamesh=None, - posttest=None, - type_init=None, - synth_info=None, - theta_mle=None, -): - """ - Smat3D : ncand x nfield x nfield (3D) Emulator Covariance matrix - pred_mean : ncand x nfield (2D) Emulator Mean matrix - rVh_1_3d : ncand x nfield x nacquired (3D) matrix - """ - - p = theta.shape[1] - dt = thetamesh.shape[1] - dx = x.shape[1] - n_x = x.shape[0] - - xuniq = np.unique(x, axis=0) - # if synth_info.data_name == 'covid19': - # clist = construct_candlist_covid(thetalimits, xuniq, prior_func, prior_func_t) - # else: - clist = construct_candlist(thetalimits, xuniq, prior_func, prior_func_t) - - emupred = emu.predict(x=np.arange(0, 1)[:, None], theta=clist) - emuvar = emupred.var() - th_cand = clist[np.argmax(emuvar.flatten()), :].reshape(1, p) - return th_cand - - -def construct_candlist(thetalimits, xuniq, prior_func, prior_func_t): - - # Create a candidate list - sampling = LHS(xlimits=thetalimits) - clist = sampling(1500) - return clist diff --git a/PUQ/designmethods/gen_funcs/EI.py b/PUQ/designmethods/gen_funcs/EI.py deleted file mode 100644 index 99447e3..0000000 --- a/PUQ/designmethods/gen_funcs/EI.py +++ /dev/null @@ -1,104 +0,0 @@ -import numpy as np -import scipy - - -def eifunc(clist, x, obs, obsvar, emu, delta): - - ei_val = np.zeros(len(clist)) - pp = emu.predict(x, clist) - ppmean = pp.mean() - ppvar = pp.var() - - for l in range(ppmean.shape[0]): - diff = obs[0, l] - ppmean[l, :] - sumvar = np.diag(obsvar)[l] + ppvar[l, :] - - p2 = 1 - scipy.stats.norm.cdf((delta[l] - diff) / np.sqrt(sumvar), 0, 1) - i2 = diff - delta[l] - pdfval = scipy.stats.norm.pdf((delta[l] - diff) / np.sqrt(sumvar), 0, 1) - r2 = np.sqrt(sumvar) * pdfval - - p1 = scipy.stats.norm.cdf((-delta[l] - diff) / np.sqrt(sumvar), 0, 1) - i1 = -diff - delta[l] - pdfval = scipy.stats.norm.pdf((-delta[l] - diff) / np.sqrt(sumvar), 0, 1) - r1 = np.sqrt(sumvar) * pdfval - - ei_val += -1 * ((p1 * i1 + r1) + (p2 * i2 + r2)) - - return ei_val - - -def ei( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, - believer=0, - candsize=100, - refsize=100, -): - - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - fevals_c = fevals[:, ~idnan] - - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - p = theta.shape[1] - theta_acq, f_acq = [], [] - - # Best error - error = np.sum(np.abs(obs - fevals_c.T), axis=1) - delta = np.abs(obs - fevals_c.T)[np.argmin(error)] - liar = np.mean(fevals_c) - - for i in range(n): - clist = prior_func.rnd(candsize, None) - acq_val = eifunc(clist, x, obs, obsvar, emu, delta) - - if n == 1: - idc = np.argmax(acq_val) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - break - else: - if believer == 0: - idc = np.argsort(acq_val)[::-1][:n] - ctheta = clist[idc, :].reshape((n, p)) - theta_acq.append(ctheta) - break - - elif believer == 1: - idc = np.argmax(acq_val) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - clist = np.delete(clist, idc, 0) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - elif believer == 2: - idc = np.argmax(acq_val) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - clist = np.delete(clist, idc, 0) - - # Constant-liar strategy - fevalsnew = liar.reshape(1, 1) - emu.update(theta=ctheta, f=fevalsnew) - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq diff --git a/PUQ/designmethods/gen_funcs/EIVAR.py b/PUQ/designmethods/gen_funcs/EIVAR.py deleted file mode 100644 index 8d353f0..0000000 --- a/PUQ/designmethods/gen_funcs/EIVAR.py +++ /dev/null @@ -1,105 +0,0 @@ -import numpy as np -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - compute_postvar, - compute_eivar, - multiple_pdfs, - get_emuvar, -) -from smt.sampling_methods import LHS -import scipy - - -def eivar( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, - believer=None, - candsize=None, - refsize=None, -): - - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - theta_uc = theta[idnan, :] - fevals_c = fevals[:, ~idnan] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - theta_acq = [] - n_x, p = x.shape[0], theta.shape[1] - obsvar3d = obsvar.reshape(1, n_x, n_x) - - liar = np.mean(fevals_c) - - # print(fevals_c.shape) - - for i in range(n): - clist = prior_func.rnd(candsize, None) - - emupredict = emu.predict(x, thetatest) - emumean = emupredict.mean() - emumeanT = emumean.T - emuvar, is_cov = get_emuvar(emupredict) - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - - # Get the n_ref x d x d x n_cand phi matrix - emuphi4d = emu.acquisition(x=x, theta1=thetatest, theta2=clist) - acq_func = [] - - # Pass over all the candidates - for c_id in range(len(clist)): - posteivar = compute_eivar( - var_obsvar1[:, real_x, real_x.T], - emuphi4d[:, real_x, real_x.T, c_id], - emumeanT[:, real_x.flatten()], - emuvar[real_x, :, real_x.T], - obs, - is_cov, - posttest, - ) - acq_func.append(posteivar) - - if n == 1: - idc = np.argmin(acq_func) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - break - else: - if believer == 1: - idc = np.argmin(acq_func) - ctheta = clist[idc, :].reshape((1, p)) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - - elif believer == 2: - idc = np.argmin(acq_func) - ctheta = clist[idc, :].reshape((1, p)) - - # Constant liar strategy - fevalsnew = liar.reshape(1, 1) - emu.update(theta=ctheta, f=fevalsnew) - - theta_acq.append(ctheta) - - # clist = np.delete(clist, idc, 0) - # Kriging believer strategy - # fevalsnew = emu.predict(x=x, theta=ctheta) - # emu.update(theta=ctheta, f=fevalsnew.mean()) - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq diff --git a/PUQ/designmethods/gen_funcs/HYBRID_EI.py b/PUQ/designmethods/gen_funcs/HYBRID_EI.py deleted file mode 100644 index a3132fa..0000000 --- a/PUQ/designmethods/gen_funcs/HYBRID_EI.py +++ /dev/null @@ -1,120 +0,0 @@ -import numpy as np -import scipy -from PUQ.designmethods.gen_funcs.EI import eifunc -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - compute_postvar, - compute_eivar, - multiple_pdfs, - get_emuvar, -) -from smt.sampling_methods import LHS - - -def eivarfunc(clist, x, real_x, thetatest, refsize, obs, obsvar3d, emu, priortest): - ids = np.random.choice(len(thetatest), refsize, replace=False) - thetaref = thetatest[ids, :] - emupredict = emu.predict(x, thetaref) - emumean = emupredict.mean() - emumeanT = emumean.T - emuvar, is_cov = get_emuvar(emupredict) - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - - # Get the n_ref x d x d x n_cand phi matrix - emuphi4d = emu.acquisition(x=x, theta1=thetaref, theta2=clist) - acq_func = [] - - # Pass over all the candidates - for c_id in range(len(clist)): - posteivar = compute_eivar( - var_obsvar1[:, real_x, real_x.T], - emuphi4d[:, real_x, real_x.T, c_id], - emumeanT[:, real_x.flatten()], - emuvar[real_x, :, real_x.T], - obs, - is_cov, - priortest, - ) - acq_func.append(-1 * posteivar) - - return acq_func - - -def hybrid_ei( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, - believer=0, - candsize=100, - refsize=100, -): - - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - fevals_c = fevals[:, ~idnan] - - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - n_x, p = x.shape[0], theta.shape[1] - theta_acq = [] - obsvar3d = obsvar.reshape(1, n_x, n_x) - - theta_acq, f_acq = [], [] - is_ei = True if (len(theta) % 2) == 0 else False - - # Best error - error = np.sum(np.abs(obs - fevals_c.T), axis=1) - delta = np.abs(obs - fevals_c.T)[np.argmin(error)] - liar = np.mean(fevals_c) - for i in range(n): - clist = prior_func.rnd(candsize, None) - - if is_ei: - acq_val = eifunc(clist, x, obs, obsvar, emu, delta) - is_ei = False - else: - acq_val = eivarfunc( - clist, x, real_x, thetatest, refsize, obs, obsvar3d, emu, posttest - ) - is_ei = True - - if n == 1: - idc = np.argmax(acq_val) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - break - else: - if believer == 1: - idc = np.argmax(acq_val) - ctheta = clist[idc, :].reshape((1, p)) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - - elif believer == 2: - idc = np.argmax(acq_val) - ctheta = clist[idc, :].reshape((1, p)) - - # Constant liar strategy - fevalsnew = liar.reshape(1, 1) - emu.update(theta=ctheta, f=fevalsnew) - - theta_acq.append(ctheta) - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq diff --git a/PUQ/designmethods/gen_funcs/MAXEXP.py b/PUQ/designmethods/gen_funcs/MAXEXP.py deleted file mode 100644 index cd65bdc..0000000 --- a/PUQ/designmethods/gen_funcs/MAXEXP.py +++ /dev/null @@ -1,90 +0,0 @@ -import numpy as np -from sklearn.metrics import pairwise_distances -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - compute_postvar, - compute_eivar, - multiple_pdfs, - get_emuvar, -) -from smt.sampling_methods import LHS -import scipy - - -def maxexp( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, - believer=None, - candsize=None, - refsize=None, -): - - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - theta_acq, f_acq = [], [] - d, p = x.shape[0], theta.shape[1] - obsvar3d = obsvar.reshape(1, d, d) - - # Create a candidate list. - n_clist = 100 * n - if type_init == "LHS": - sampling = LHS(xlimits=thetalimits) - clist = sampling(n_clist) - else: - clist = prior_func.rnd(n_clist, None) - - theta_sd = (theta - thetalimits[:, 0]) / (thetalimits[:, 1] - thetalimits[:, 0]) - theta_cand_sd = (clist - thetalimits[:, 0]) / ( - thetalimits[:, 1] - thetalimits[:, 0] - ) - - for j in range(n): - - emupredict = emu.predict(x, clist) - emumean = emupredict.mean() - emuvar, is_cov = get_emuvar(emupredict) - emumeanT = emumean.T - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - postmean = multiple_pdfs( - obs, emumeanT[:, real_x.flatten()], var_obsvar1[:, real_x, real_x.T] - ) - logpostmean = np.log(postmean) - - # Diversity term - d = pairwise_distances(theta_sd, theta_cand_sd, metric="euclidean") - min_dist = np.min(d, axis=0) - - # Acquisition function - acq_func = logpostmean + np.log(min_dist) - - # New theta - idc = np.argmax(acq_func) - theta_sd = np.concatenate((theta_sd, theta_cand_sd[idc][None, :]), axis=0) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - f_acq.append(postmean[idc]) - clist = np.delete(clist, idc, 0) - theta_cand_sd = np.delete(theta_cand_sd, idc, 0) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - - theta_acq = np.array(theta_acq).reshape((n, p)) - return theta_acq diff --git a/PUQ/designmethods/gen_funcs/MAXVAR.py b/PUQ/designmethods/gen_funcs/MAXVAR.py deleted file mode 100644 index 9d65156..0000000 --- a/PUQ/designmethods/gen_funcs/MAXVAR.py +++ /dev/null @@ -1,85 +0,0 @@ -import numpy as np -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - compute_postvar, - multiple_pdfs, - get_emuvar, -) -from smt.sampling_methods import LHS -import scipy - - -def maxvar( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, - believer=None, - candsize=None, - refsize=None, -): - - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - theta_acq, f_acq = [], [] - d, p = x.shape[0], theta.shape[1] - obsvar3d = obsvar.reshape(1, d, d) - diags = np.diag(obsvar[real_x, real_x.T]) - d_real = real_x.shape[0] - coef = (2**d_real) * (np.sqrt(np.pi) ** d_real) * np.sqrt(np.prod(diags)) - - # Create a candidate list. - n_clist = 100 * n - if type_init == "LHS": - sampling = LHS(xlimits=thetalimits) - clist = sampling(n_clist) - else: - clist = prior_func.rnd(n_clist, None) - - # Acquire n thetas. - for i in range(n): - emupredict = emu.predict(x, clist) - emumean = emupredict.mean() - emuvar, is_cov = get_emuvar(emupredict) - emumeanT = emumean.T - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - var_obsvar2 = emuvarT + 0.5 * obsvar3d - postmean = multiple_pdfs( - obs, emumeanT[:, real_x.flatten()], var_obsvar1[:, real_x, real_x.T] - ) - postvar = compute_postvar( - obs, - emumeanT[:, real_x.flatten()], - var_obsvar1[:, real_x, real_x.T], - var_obsvar2[:, real_x, real_x.T], - coef, - ) - - acq_func = postvar.copy() - idc = np.argmax(acq_func) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - f_acq.append(postmean[idc]) - clist = np.delete(clist, idc, 0) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq diff --git a/PUQ/designmethods/gen_funcs/PI.py b/PUQ/designmethods/gen_funcs/PI.py deleted file mode 100644 index 0345645..0000000 --- a/PUQ/designmethods/gen_funcs/PI.py +++ /dev/null @@ -1,89 +0,0 @@ -import numpy as np -import scipy - - -def pifunc(clist, x, obs, obsvar, emu, delta): - pi_val = np.zeros(len(clist)) - pp = emu.predict(x, clist) - ppmean = pp.mean() - ppvar = pp.var() - - for l in range(ppmean.shape[0]): - diff = obs[0, l] - ppmean[l, :] - sumvar = np.diag(obsvar)[l] + ppvar[l, :] - - part1 = scipy.stats.norm.cdf((delta[l] - diff) / np.sqrt(sumvar), 0, 1) - part2 = scipy.stats.norm.cdf((-delta[l] - diff) / np.sqrt(sumvar), 0, 1) - pi_val += part1 - part2 - - return pi_val - - -def pi( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, - believer=0, - candsize=100, - refsize=100, -): - - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - fevals_c = fevals[:, ~idnan] - - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - p = theta.shape[1] - theta_acq, f_acq = [], [] - - # Best error - error = np.sum(np.abs(obs - fevals_c.T), axis=1) - delta = np.abs(obs - fevals_c.T)[np.argmin(error)] - liar = np.mean(fevals_c) - - for i in range(n): - clist = prior_func.rnd(candsize, None) - - acq_val = pifunc(clist, x, obs, obsvar, emu, delta) - if believer == 0: - idc = np.argsort(acq_val)[::-1][:n] - ctheta = clist[idc, :].reshape((n, p)) - theta_acq.append(ctheta) - break - - elif believer == 1: - idc = np.argmax(acq_val) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - clist = np.delete(clist, idc, 0) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - elif believer == 2: - idc = np.argmax(acq_val) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - clist = np.delete(clist, idc, 0) - - # Kriging believer strategy - fevalsnew = liar.reshape(1, 1) - emu.update(theta=ctheta, f=fevalsnew) - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq diff --git a/PUQ/designmethods/gen_funcs/RND.py b/PUQ/designmethods/gen_funcs/RND.py deleted file mode 100644 index 3255c0c..0000000 --- a/PUQ/designmethods/gen_funcs/RND.py +++ /dev/null @@ -1,21 +0,0 @@ -def rnd( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, - believer=None, - candsize=None, - refsize=None, -): - - theta_acq = prior_func.rnd(n, None) - return theta_acq diff --git a/PUQ/designmethods/gen_funcs/acquisition_1d_deterministic.py b/PUQ/designmethods/gen_funcs/acquisition_1d_deterministic.py new file mode 100644 index 0000000..9b89895 --- /dev/null +++ b/PUQ/designmethods/gen_funcs/acquisition_1d_deterministic.py @@ -0,0 +1,347 @@ +import numpy as np +from scipy.stats import qmc +from PUQ.designmethods.support import multiple_pdfs, multiple_determinants +from hetgpy.IMSE import crit_IMSPE, Wij, IMSPE, allocate_mult +import emcee +from copy import deepcopy + + +def generate_neighborhood(acq): + + rand = np.random.default_rng(acq.seed) + neigh_type = acq.args.get("neighbor", None) + + if neigh_type == "LHS": + N = int(acq.nL) + sampling = qmc.LatinHypercube(d=acq.zlim.shape[0], seed=int(acq.seed)) + L = sampling.random(n=N) + else: + N = int(acq.nL * 0.5) + sampling = qmc.LatinHypercube(d=acq.zlim.shape[0], seed=int(acq.seed)) + L_explore = sampling.random(n=N) + + sampling = qmc.LatinHypercube(d=acq.tlim.shape[0], seed=int(acq.seed)) + Lt = sampling.random(n=N) + + num_options = len(acq.x) + + # Distribute the rows as evenly as possible + counts = np.full(num_options, N // num_options) # Base count for each row type + counts[ + rand.choice(num_options, N % num_options, replace=False) + ] += 1 # Assign extra rows randomly + + # Create the array by stacking the chosen rows (Fixed: Explicitly convert to a list) + Lx = np.vstack( + [row for i in range(num_options) for row in [acq.x[i]] * counts[i]] + ) + + # Shuffle the rows randomly + rand.shuffle(Lx) + + L_exploit = np.concatenate((Lx, Lt), axis=1) + + L = np.concatenate((L_explore, L_exploit)) + + return L + + +class acquisition_function: + def __init__(self, model, cls_func, args): + self.model = model # deepcopy(model) # model.copy() + self.cls_func = cls_func + # self.persis_info = persis_info + self.args = args + self.x = self.cls_func.x + self.d = self.cls_func.d + self.p = self.cls_func.p + self.dx = self.cls_func.dx + self.dt = self.cls_func.dt + self.zlim = cls_func.zlim + self.xlim = cls_func.zlim[0 : cls_func.dx, :] + self.tlim = cls_func.zlim[cls_func.dx : cls_func.p, :] + self.y = self.cls_func.real_data + self.Sigma = self.cls_func.obsvar + self.Sigma3d = self.Sigma.reshape(1, self.d, self.d) + self.detSigma = np.linalg.det(self.Sigma) + self.twopid = (2**self.d) * (np.sqrt(np.pi) ** self.d) + self.twopiddet = ( + (2**self.d) * (np.sqrt(np.pi) ** self.d) * np.sqrt(self.detSigma) + ) + + def acquire_new(self): + + return eval("self.evaluate")(self.args.get("new", True), return_pseudo=False) + + def gen_pred(self, model, x, t, return_id=False, return_flat=False): + nm, d = t.shape[0], x.shape[0] + ntot = nm * d + + # print(t.shape) + # (ntot, d) + x_tiled = np.tile(x, (t.shape[0], 1)) + # (ntot, p-d) + t_repeated = np.repeat(t, x.shape[0], axis=0) + # (ntot, p) + z = np.hstack([x_tiled, t_repeated]) + + # to construct S matrix + id_row = np.arange(0, ntot) + id_col = np.arange(0, ntot).reshape(nm, d) + id_col = np.repeat(id_col, repeats=d, axis=0) + + # predict at mesh + meshPr = model.predict(x=z, thetaprime=z) + + # ntot, ntot x ntot, ntot + # mu, Sn, sd2 = meshPr["mean"], meshPr["cov"], meshPr["sd2"] + mu, Sn, sd2 = meshPr._info["mean"], meshPr._info["covmat"], meshPr._info["var"] + + muT = mu.reshape(nm, d) + S = Sn[id_row[:, None], id_col].reshape(nm, d, d) + + if return_id: + return muT, S, z, id_row[:, None], id_col, mu[:, None] + else: + if return_flat: + return muT, S, z, mu[:, None] + else: + return muT, S, z + + def crit_pvar(self, model, x, t): + + # posterior variance + mu, S, z = self.gen_pred(model, x, t) + + M = S + 0.5 * self.Sigma3d + N = S + self.Sigma3d + + f = multiple_pdfs(self.y, mu, M) + g = multiple_pdfs(self.y, mu, N) + + vals = (1 / self.twopiddet) * f - g**2 + + return vals + + def total_var(self, model, x, t): + vals = self.crit_pvar(model=model, x=x, t=t) + return np.mean(self.weights * vals) + + +class var(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", True) + self.nL = args.get("nL", 100) + + if args.get("integral") == "importance": + # print("Importance sampling") + self.epsilon = args.get("epsilon", 10 ** (-10)) + self.discard = args.get("discard", 100) + self.nsteps = args.get("nsteps", 200) + self.thin = args.get("thin", 20) + self.nwalkers = args.get("nwalkers", 20) + self.reference_set() + elif args.get("integral") == "LHS": + print("LHS") + sampling = LHS(xlimits=self.tlim, random_state=int(self.seed)) + self.t_ref = sampling(500) + self.weights = 1 + else: + self.t_ref = args.get("t_grid") + self.weights = 1 + + def reference_set(self): + def log_probability(ctheta): + if np.any((ctheta < 0) | (ctheta > 1)): + return -np.inf + else: + pvar = self.crit_pvar(model=self.model, x=self.x, t=ctheta[None, :]) + + pvar = max(pvar, 0) + return np.log(pvar) + + def sample(ndim, nwalkers): + np.random.seed(int(self.seed)) + sampler = emcee.EnsembleSampler(nwalkers, ndim, log_probability) + + sampling = LHS(xlimits=self.tlim, random_state=int(self.seed)) + loc0 = sampling(nwalkers) + + sampler.run_mcmc(initial_state=loc0, nsteps=self.nsteps, progress=False) + + samples = sampler.get_chain(discard=self.discard, thin=self.thin, flat=True) + return samples + + def importance_weight(theta): + pvar = self.crit_pvar(model=self.model, x=self.x, t=theta) + pvar = pvar + self.epsilon + unnorm_weight = 1 / pvar + weight = unnorm_weight / np.sum(unnorm_weight) + return weight + + self.t_ref = sample(ndim=self.dt, nwalkers=self.nwalkers) + self.weights = importance_weight(theta=self.t_ref) + + def evaluate(self, new, return_pseudo): + + L = generate_neighborhood(self) + new_input = self.evaluate_explore(L) + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + vals = self.crit_pvar(model=self.model, x=self.x, t=L[:, self.dx : self.p]) + + new_input = L[np.argmax(vals), :].reshape(1, self.p) + return new_input + + +class imse(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", "both") + self.nL = args.get("nL", 100) + self.t_grid = args.get("t_grid", None) + + def evaluate(self, new, return_pseudo): + + sampling = LHS(xlimits=self.zlim, random_state=int(self.seed)) + L = sampling(self.nL) + new_input = self.evaluate_explore(L) + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + IMSPE_grid = np.array([crit_IMSPE(x, model=self.model) for x in L]) + new_input = L[np.argmin(IMSPE_grid), :].reshape(1, self.cls_func.p) + return new_input + + +class ivar(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", "both") + self.nL = args.get("nL", 100) + if args.get("integral") == "importance": + # print("Importance sampling") + self.epsilon = args.get("epsilon", 10 ** (-10)) + self.discard = args.get("discard", 100) + self.nsteps = args.get("nsteps", 200) + self.thin = args.get("thin", 20) + self.nwalkers = args.get("nwalkers", 20) + self.reference_set() + elif args.get("integral") == "LHS": + sampling = qmc.LatinHypercube(d=self.tlim.shape[0], seed=int(self.seed)) + self.t_ref = sampling.random(n=500) + self.weights = 1 + else: + self.t_ref = args.get("t_grid") + self.weights = 1 + + def reference_set(self): + def log_probability(ctheta): + if np.any((ctheta < 0) | (ctheta > 1)): + return -np.inf + else: + pvar = self.crit_pvar(model=self.model, x=self.x, t=ctheta[None, :]) + + pvar = max(pvar, 0) + return np.log(pvar) + + def sample(ndim, nwalkers): + np.random.seed(int(self.seed)) + sampler = emcee.EnsembleSampler(nwalkers, ndim, log_probability) + + sampling = qmc.LatinHypercube(d=self.tlim.shape[0], seed=int(self.seed)) + loc0 = sampling.random(n=nwalkers) + + sampler.run_mcmc(initial_state=loc0, nsteps=self.nsteps, progress=False) + + samples = sampler.get_chain(discard=self.discard, thin=self.thin, flat=True) + return samples + + def importance_weight(theta): + pvar = self.crit_pvar(model=self.model, x=self.x, t=theta) + pvar = pvar + self.epsilon + unnorm_weight = 1 / pvar + weight = unnorm_weight / np.sum(unnorm_weight) + return weight + + self.t_ref = sample(ndim=self.dt, nwalkers=self.nwalkers) + self.weights = importance_weight(theta=self.t_ref) + + def evaluate(self, new, return_pseudo): + + L = generate_neighborhood(self) + new_input = self.evaluate_explore(L) + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + + nm = self.t_ref.shape[0] + + vals = np.zeros(self.nL) + + mu, S, z = self.gen_pred(self.model, self.x, self.t_ref) + + V1 = S + self.Sigma3d + + # predict at candidates + candPr = self.model.predict(x=L, thetaprime=z) + + # nL, nL, nL x ntot + # candvar, candnugs, candcov = candPr["sd2"], candPr["nugs"], candPr["cov"] + candvar, candnugs, candcov = ( + candPr._info["var"], + candPr._info["nugs"], + candPr._info["covmat"], + ) + candtotvat = candvar + candnugs + + for k in range(0, self.nL): + # (mn x d x 1) + covc = candcov[k, :].reshape(nm, self.d, 1) + + # (mn x 1 x d) + covcT = np.transpose(covc, (0, 2, 1)) + + # (nm, d, d) + phic = (covc * covcT) / candtotvat[k] + + # C1: nm x d x d, C2: nm x d x d + C1 = (V1 + phic) * 0.5 + C2 = V1 - phic + + rpdf = multiple_pdfs(self.y, mu, C1) + dets = multiple_determinants(C2) + + part2 = (1 / self.twopid) * (rpdf / np.sqrt(dets)) + + vals[k] = np.sum(self.weights * part2) + + new_input = L[np.argmax(vals), :].reshape(1, self.p) + + return new_input diff --git a/PUQ/designmethods/gen_funcs/acquisition_1d_stochastic.py b/PUQ/designmethods/gen_funcs/acquisition_1d_stochastic.py new file mode 100644 index 0000000..78606a2 --- /dev/null +++ b/PUQ/designmethods/gen_funcs/acquisition_1d_stochastic.py @@ -0,0 +1,765 @@ +import numpy as np +from scipy.stats import qmc +from PUQ.designmethods.support import multiple_pdfs, multiple_determinants +from hetgpy.covariance_functions import cov_gen +from hetgpy.IMSE import crit_IMSPE, Wij, IMSPE, allocate_mult +import emcee +from PUQ.designmethods.gen_funcs.allocate_reps_1d import allocate +import copy + + +def generate_neighborhood(acq): + + rand = np.random.default_rng(acq.seed) + neigh_type = acq.args.get("neighbor", None) + + if neigh_type == "LHS": + N = int(acq.nL) + sampling = qmc.LatinHypercube(d=acq.zlim.shape[0], seed=int(acq.seed)) + L = sampling.random(n=N) + else: + N = int(acq.nL * 0.5) + + sampling = qmc.LatinHypercube(d=acq.zlim.shape[0], seed=int(acq.seed)) + L_explore = sampling.random(n=N) + + sampling = qmc.LatinHypercube(d=acq.tlim.shape[0], seed=int(acq.seed)) + Lt = sampling.random(n=N) + + num_options = len(acq.x) + + # Distribute the rows as evenly as possible + counts = np.full(num_options, N // num_options) # Base count for each row type + counts[ + rand.choice(num_options, N % num_options, replace=False) + ] += 1 # Assign extra rows randomly + + # Create the array by stacking the chosen rows (Fixed: Explicitly convert to a list) + Lx = np.vstack( + [row for i in range(num_options) for row in [acq.x[i]] * counts[i]] + ) + + # Shuffle the rows randomly + rand.shuffle(Lx) + + L_exploit = np.concatenate((Lx, Lt), axis=1) + + L = np.concatenate((L_explore, L_exploit)) + + return L + + +class acquisition_function: + def __init__(self, model, cls_func, args): + self.model = copy.deepcopy(model) # deepcopy(model) # model.copy() + self.cls_func = cls_func + # self.persis_info = persis_info + self.args = args + self.x = self.cls_func.x + self.d = self.cls_func.d + self.p = self.cls_func.p + self.dx = self.cls_func.dx + self.dt = self.cls_func.dt + self.zlim = cls_func.zlim + self.xlim = cls_func.zlim[0 : cls_func.dx, :] + self.tlim = cls_func.zlim[cls_func.dx : cls_func.p, :] + self.y = self.cls_func.real_data + self.Sigma = self.cls_func.obsvar + self.Sigma3d = self.Sigma.reshape(1, self.d, self.d) + self.detSigma = np.linalg.det(self.Sigma) + self.twopid = (2**self.d) * (np.sqrt(np.pi) ** self.d) + self.twopiddet = ( + (2**self.d) * (np.sqrt(np.pi) ** self.d) * np.sqrt(self.detSigma) + ) + + def acquire_new(self): + + if isinstance(self, lookahead): + return eval("self.evaluate")() + + else: + return eval("self.evaluate")( + self.args.get("new", True), return_pseudo=False + ) + + def gen_pred(self, model, x, t, return_id=False, return_flat=False): + nm, d = t.shape[0], x.shape[0] + ntot = nm * d + + # print(t.shape) + # (ntot, d) + x_tiled = np.tile(x, (t.shape[0], 1)) + # (ntot, p-d) + t_repeated = np.repeat(t, x.shape[0], axis=0) + # (ntot, p) + z = np.hstack([x_tiled, t_repeated]) + + # to construct S matrix + id_row = np.arange(0, ntot) + id_col = np.arange(0, ntot).reshape(nm, d) + id_col = np.repeat(id_col, repeats=d, axis=0) + + # predict at mesh + meshPr = model.predict(x=z, thetaprime=z) + + # ntot, ntot x ntot, ntot + # mu, Sn, sd2 = meshPr["mean"], meshPr["cov"], meshPr["sd2"] + mu, Sn, sd2 = meshPr._info["mean"], meshPr._info["covmat"], meshPr._info["var"] + + muT = mu.reshape(nm, d) + S = Sn[id_row[:, None], id_col].reshape(nm, d, d) + + if return_id: + return muT, S, z, id_row[:, None], id_col, mu[:, None] + else: + if return_flat: + return muT, S, z, mu[:, None] + else: + return muT, S, z + + def crit_pvar(self, model, x, t): + + # posterior variance + mu, S, z = self.gen_pred(model, x, t) + + M = S + 0.5 * self.Sigma3d + N = S + self.Sigma3d + + f = multiple_pdfs(self.y, mu, M) + g = multiple_pdfs(self.y, mu, N) + + vals = (1 / self.twopiddet) * f - g**2 + + return vals + + def total_var(self, model, x, t): + vals = self.crit_pvar(model=model, x=x, t=t) + return np.mean(self.weights * vals) + + +class var(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", True) + self.nL = args.get("nL", 100) + + if args.get("integral") == "importance": + # print("Importance sampling") + self.epsilon = args.get("epsilon", 10 ** (-10)) + self.discard = args.get("discard", 100) + self.nsteps = args.get("nsteps", 200) + self.thin = args.get("thin", 20) + self.nwalkers = args.get("nwalkers", 20) + self.reference_set() + elif args.get("integral") == "LHS": + print("LHS") + sampling = LHS(xlimits=self.tlim, random_state=int(self.seed)) + self.t_ref = sampling(500) + self.weights = 1 + else: + self.t_ref = args.get("t_grid") + self.weights = 1 + + def reference_set(self): + def log_probability(ctheta): + if np.any((ctheta < 0) | (ctheta > 1)): + return -np.inf + else: + pvar = self.crit_pvar(model=self.model, x=self.x, t=ctheta[None, :]) + + pvar = max(pvar, 0) + return np.log(pvar) + + def sample(ndim, nwalkers): + np.random.seed(int(self.seed)) + sampler = emcee.EnsembleSampler(nwalkers, ndim, log_probability) + + sampling = LHS(xlimits=self.tlim, random_state=int(self.seed)) + loc0 = sampling(nwalkers) + + sampler.run_mcmc(initial_state=loc0, nsteps=self.nsteps, progress=False) + + samples = sampler.get_chain(discard=self.discard, thin=self.thin, flat=True) + return samples + + def importance_weight(theta): + pvar = self.crit_pvar(model=self.model, x=self.x, t=theta) + pvar = pvar + self.epsilon + unnorm_weight = 1 / pvar + weight = unnorm_weight / np.sum(unnorm_weight) + return weight + + self.t_ref = sample(ndim=self.dt, nwalkers=self.nwalkers) + self.weights = importance_weight(theta=self.t_ref) + + def evaluate(self, new, return_pseudo): + + if new: + L = generate_neighborhood(self) + new_input = self.evaluate_explore(L) + else: + L = self.model["X0"] + new_input = self.evaluate_exploit(L) + + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + vals = self.crit_pvar(model=self.model, x=self.x, t=L[:, self.dx : self.p]) + + new_input = L[np.argmax(vals), :].reshape(1, self.p) + return new_input + + def evaluate_exploit(self, L): + vals = self.crit_pvar(model=self.model, x=self.x, t=L[:, self.dx : self.p]) + + new_input = L[np.argmax(vals), :].reshape(1, self.p) + return new_input + + +class imse(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", "both") + self.nL = args.get("nL", 100) + self.t_grid = args.get("t_grid", None) + + def evaluate(self, new, return_pseudo): + + if new: + sampling = LHS(xlimits=self.zlim, random_state=int(self.seed)) + L = sampling(self.nL) + new_input = self.evaluate_explore(L) + else: + L = self.model["X0"] + new_input = self.evaluate_exploit(L) + + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + IMSPE_grid = np.array([crit_IMSPE(x, model=self.model) for x in L]) + new_input = L[np.argmin(IMSPE_grid), :].reshape(1, self.cls_func.p) + return new_input + + def evaluate_exploit(self, L): + IMSPE_grid = np.array( + [crit_IMSPE(id=[x_id], model=self.model) for x_id, x in enumerate(L)] + ) + new_input = L[np.argmin(IMSPE_grid), :].reshape(1, self.cls_func.p) + return new_input + + +class ivar(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", "both") + self.nL = args.get("nL", 100) + if args.get("integral") == "importance": + # print("Importance sampling") + self.epsilon = args.get("epsilon", 10 ** (-10)) + self.discard = args.get("discard", 100) + self.nsteps = args.get("nsteps", 200) + self.thin = args.get("thin", 20) + self.nwalkers = args.get("nwalkers", 20) + self.reference_set() + elif args.get("integral") == "LHS": + sampling = qmc.LatinHypercube(d=self.tlim.shape[0], seed=int(self.seed)) + self.t_ref = sampling.random(n=500) + self.weights = 1 + else: + self.t_ref = args.get("t_grid") + self.weights = 1 + + def reference_set(self): + def log_probability(ctheta): + if np.any((ctheta < 0) | (ctheta > 1)): + return -np.inf + else: + pvar = self.crit_pvar(model=self.model, x=self.x, t=ctheta[None, :]) + + pvar = max(pvar, 0) + return np.log(pvar) + + def sample(ndim, nwalkers): + np.random.seed(int(self.seed)) + sampler = emcee.EnsembleSampler(nwalkers, ndim, log_probability) + + sampling = qmc.LatinHypercube(d=self.tlim.shape[0], seed=int(self.seed)) + loc0 = sampling.random(n=nwalkers) + + sampler.run_mcmc(initial_state=loc0, nsteps=self.nsteps, progress=False) + + samples = sampler.get_chain(discard=self.discard, thin=self.thin, flat=True) + return samples + + def importance_weight(theta): + pvar = self.crit_pvar(model=self.model, x=self.x, t=theta) + pvar = pvar + self.epsilon + unnorm_weight = 1 / pvar + weight = unnorm_weight / np.sum(unnorm_weight) + return weight + + self.t_ref = sample(ndim=self.dt, nwalkers=self.nwalkers) + self.weights = importance_weight(theta=self.t_ref) + + def evaluate(self, new, return_pseudo): + + if new: + L = generate_neighborhood(self) + new_input = self.evaluate_explore(L) + else: + L = self.model._info["X0"] # self.model["X0"] + new_input = self.evaluate_exploit(L) + + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + # fnew = np.array([pnew["mean"]])[None, :] + fnew = np.array([pnew._info["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + + nm = self.t_ref.shape[0] + + vals = np.zeros(self.nL) + + mu, S, z = self.gen_pred(self.model, self.x, self.t_ref) + + V1 = S + self.Sigma3d + + # for i in np.arange(S.shape[0]): + # print(np.diag(S[i, :, :])) + + # predict at candidates + candPr = self.model.predict(x=L, thetaprime=z) + + # nL, nL, nL x ntot + # candvar, candnugs, candcov = candPr["sd2"], candPr["nugs"], candPr["cov"] + candvar, candnugs, candcov = ( + candPr._info["var"], + candPr._info["nugs"], + candPr._info["covmat"], + ) + candtotvat = candvar + candnugs + + for k in range(0, self.nL): + # (mn x d x 1) + covc = candcov[k, :].reshape(nm, self.d, 1) + + # (mn x 1 x d) + covcT = np.transpose(covc, (0, 2, 1)) + + # (nm, d, d) + phic = (covc * covcT) / candtotvat[k] + + # C1: nm x d x d, C2: nm x d x d + C1 = (V1 + phic) * 0.5 + C2 = V1 - phic + + rpdf = multiple_pdfs(self.y, mu, C1) + dets = multiple_determinants(C2) + + part2 = (1 / self.twopid) * (rpdf / np.sqrt(dets)) + + vals[k] = np.sum(self.weights * part2) + + new_input = L[np.argmax(vals), :].reshape(1, self.p) + return new_input + + def evaluate_exploit(self, L): + + nL = L.shape[0] + + nm = self.t_ref.shape[0] + vals = np.zeros(nL) + nrep = 1 + + mu, S, z, idr, idc, muf = self.gen_pred( + self.model, self.x, self.t_ref, return_id=True + ) + + V1 = S + self.Sigma3d + V2 = S + 0.5 * self.Sigma3d + + # Ki, mult = self.model.Ki, self.model.mult + Ki, mult = self.model._info["Ki"], self.model._info["mult"] + + candPr = self.model.predict( + x=self.model._info["X0"] + ) # self.model.predict(x=self.model["X0"]) + # candmean, candvar, candnugs = candPr["mean"], candPr["sd2"], candPr["nugs"] + candmean, candvar, candnugs = ( + candPr._info["mean"], + candPr._info["var"], + candPr._info["nugs"], + ) + smean = self.model._info["Z0"] # self.model["Z0"] + + # compute B + # if self.model.get("Lambda") is None: + if self.model._info.get("Lambda") is None: + tmp = self.model.g + tmp = np.repeat(tmp, nL) + else: + tmp = self.model._info["Lambda"] + + denom = (mult * (mult + nrep)) / (nrep * tmp) - np.diag(Ki) + Ki3D = Ki[:, :, None] * Ki[:, None, :] + B = Ki3D / denom[:, None, None] + KB = self.model._info["Ki"] + B + + # ntot x ntr + kx = cov_gen( + X1=z, + X2=self.model._info["X0"], + theta=self.model._info["theta"], + type=self.model._info["covtype"], + ) + + # coefficients to be used + ap1 = mult + 1 + mean_coefs = (candmean - smean) / ap1 + var_coefs = (candvar + candnugs) / (ap1**2) + smean = smean[:, None] + + KBe_all = KB[np.arange(nL), :, np.arange(nL)] + kKBe_all = KBe_all @ kx.T + + B_kx = np.einsum("ijk, bk -> ijb", B, kx) + phi_all = self.model._info["nu_hat"] * np.einsum("aj, ijb -> aib", kx, B_kx) + + Bs = B @ smean + c1, c2 = (1 / self.twopiddet), (1 / self.twopid) + for k in range(0, nL): + + phi_k = phi_all[idr, k, idc].reshape(nm, self.d, self.d) + + KBe_k = KB[k, :, k : k + 1] + mu_k = muf + kx @ (mean_coefs[k] * KBe_k + Bs[k, :, :]) + mu_kT = mu_k.reshape(nm, self.d) + + kKBe = kKBe_all[k, :] + covc = kKBe.reshape(nm, self.d, 1) + covcT = np.transpose(covc, (0, 2, 1)) + + # nm x d x d + gamma_k = var_coefs[k] * (covc * covcT) + + M = V2 - phi_k + gamma_k + part1 = c1 * multiple_pdfs(self.y, mu_kT, M) + + C1 = V1 - phi_k + N = 0.5 * C1 + gamma_k + part2 = multiple_pdfs(self.y, mu_kT, N) + + dets1 = multiple_determinants(C1) + part2 = c2 * (1 / np.sqrt(dets1)) * part2 + + vals[k] = np.sum(self.weights * (part1 - part2)) + + # print(np.argmin(vals)) + new_input = L[np.argmin(vals), :].reshape(1, self.p) + + # import matplotlib.pyplot as plt + # plt.scatter(L[:, 1], L[:, 2], c=vals) + # plt.scatter(L[np.argmin(vals), :][1], L[np.argmin(vals), :][2], marker="*") + # plt.show() + + return new_input + + +class lookahead(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.method = args.get("method", "ivar") + self.nL = args.get("nL", 100) + self.t_grid = args.get("t_grid", None) + + def determine_h(self): + horizon = self.args.get("horizon", None) + # print(horizon) + if horizon.get("method") == "target": + if horizon.get("previous_ratio") is None: + horizon["previous_ratio"] = len(self.model._info["Z0"]) / len( + self.model._info["Z"] + ) + self.h = horizon.get("h0") + else: + target = horizon.get("target_ratio") + previous_ratio = horizon.get("previous_ratio") + current_horizon = horizon.get("h0") + + ratio = len(self.model._info["Z0"]) / len(self.model._info["Z"]) + + # Ratio increased while too small + if ratio < target and ratio < previous_ratio: + self.h = max(-1, current_horizon - 1) + + # Ratio decreased while too high + elif ratio > target and ratio > previous_ratio: + self.h = current_horizon + 1 + else: + self.h = current_horizon + + horizon["previous_ratio"] = len(self.model._info["Z0"]) / len( + self.model._info["Z"] + ) + horizon["h0"] = self.h + + elif horizon.get("method") == "adaptive": + budget = np.sum(self.model._info["mult"]) + + if self.method == "imse": + mult_star = allocate_mult(model=self.model, N=budget).astype(int) + + else: + alloc_obj = allocate( + budget, + self.model, + self.cls_func, + {"theta": self.t_ref, "weight": self.weights}, + ) + + alloc_obj.allocatereps() + mult_star = alloc_obj.reps + + # tab_input = mult_star - self.model.mult + tab_input = mult_star - self.model._info["mult"] + tab_input[tab_input < 0] = 0 + + # import matplotlib.pyplot as plt + # for label, x_count, y_count in zip(tab_input, self.model.X0[:, 1], self.model.X0[:, 2]): + # col = "blue" + # plt.annotate( + # label, + # xy=(x_count, y_count), + # xytext=(0, 0), + # textcoords="offset points", + # fontsize=12, + # color=col + # ) + # plt.show() + + rand = np.random.default_rng(self.seed) + u, counts = np.unique(tab_input, return_counts=True) + self.h = rand.choice(u, p=counts / counts.sum()) + + elif horizon.get("h0") == -1: + self.h = -1 + else: + self.h = horizon.get("h0") + + def crit_eval(self, model): + if self.method == "imse": + value = IMSPE(model) + else: + value = self.total_var(model, self.x, self.t_ref) + return value + + def evaluate(self): + + if self.args.get("long"): + self.evaluate_vl() + else: + self.evaluate_vq() + + def evaluate_vl(self): + + acq_obj = eval(self.method)( + model=self.model, + cls_func=self.cls_func, + args=self.args, + ) + + self.t_ref, self.weights = getattr(acq_obj, "t_ref", None), getattr( + acq_obj, "weights", None + ) + + # define horizon + self.determine_h() + + if self.h == -1: + acq_obj.evaluate(new=True, return_pseudo=True) + self.znew, self.new = acq_obj.znew, True + elif self.h == 0: + acq_obj.evaluate(new=True, return_pseudo=True) + z_A = acq_obj.znew + acq_obj.model.update(Xnew=z_A, Znew=acq_obj.fnew, maxit=0) + model_A = acq_obj.model.copy() + IVAR_A1 = self.crit_eval(model_A) + + acq_obj.model = self.model.copy() + acq_obj.evaluate(new=False, return_pseudo=True) + z_B = acq_obj.znew + acq_obj.model.update(Xnew=z_B, Znew=acq_obj.fnew, maxit=0) + + model_B = acq_obj.model.copy() + IVAR_B1 = self.crit_eval(model_B) + + if IVAR_A1 < IVAR_B1: + self.znew, self.new = z_A, True + return + else: + self.znew, self.new = z_B, False + return + else: + depth = self.h + 1 + paths = np.eye(depth, dtype=int).tolist() + inputs, values = [], [] + if self.args.get("parallel", None) is None: + for pid, path in enumerate(paths): + znew, vals = self.eval_branch(acq_obj, path) + inputs.append(znew) + values.append(vals) + # print(np.round(vals*1000000)) + + vals_id = np.argmin(values) + new_input = inputs[vals_id][0] + a = paths[vals_id][0] + self.znew = new_input + self.new = True if a == 1 else False + return self + else: + from joblib import Parallel, delayed + + results = Parallel(n_jobs=len(paths))( + delayed(self.eval_branch)(acq_obj, path) + for pid, path in enumerate(paths) + ) + inputs, vals = zip(*results) + vals_id = np.argmin(vals) + return inputs[vals_id][0] + + def eval_branch(self, acq_obj, action): + + acq_obj.model = self.model.copy() + + # Initialize actions + input_action = [] + + for a in action: + + new = True if a == 1 else False + + acq_obj.evaluate(new=new, return_pseudo=True) + acq_obj.model.update(Xnew=acq_obj.znew, Znew=acq_obj.fnew, maxit=0) + input_action.append(acq_obj.znew) + + value = self.crit_eval(acq_obj.model) + + return input_action, value + + def evaluate_vq(self): + designs = [] + + acq_obj = eval(self.method)( + model=self.model, + cls_func=self.cls_func, + args=self.args, + ) + + self.t_ref, self.weights = getattr(acq_obj, "t_ref", None), getattr( + acq_obj, "weights", None + ) + + # define horizon + self.determine_h() + + acq_obj.evaluate(new=True, return_pseudo=True) + z_A, f_A = acq_obj.znew, acq_obj.fnew + path_A = [{"par": z_A, "new": True}] + + if self.h == -1: + self.znew, self.new = z_A, True + return + else: + # acq_obj.model.update(Xnew=z_A, Znew=f_A, maxit=0) + acq_obj.model.update(x=z_A, Y=f_A, maxit=0) + model_A = copy.deepcopy(acq_obj.model) # acq_obj.model.copy() + IVAR_A1 = self.crit_eval(model_A) + + if self.h > 0: + + acq_obj.model = copy.deepcopy(model_A) # model_A.copy() + for i in range(0, self.h): + acq_obj.evaluate(new=False, return_pseudo=True) + path_A.append({"par": acq_obj.znew, "new": False}) + acq_obj.model.update(x=acq_obj.znew, Y=acq_obj.fnew, maxit=0) + + IVAR_A = self.crit_eval(acq_obj.model) + + designs.append({"input": z_A, "path": path_A, "value": IVAR_A}) + + if self.h == 0: + acq_obj.model = copy.deepcopy(self.model) # self.model.copy() + acq_obj.evaluate(new=False, return_pseudo=True) + z_B = acq_obj.znew + acq_obj.model.update(x=z_B, Y=acq_obj.fnew, maxit=0) + model_B = copy.deepcopy(acq_obj.model) # acq_obj.model.copy() + IVAR_B1 = self.crit_eval(model_B) + + if IVAR_A1 < IVAR_B1: + self.znew, self.new = z_A, True + return + else: + self.znew, self.new = z_B, False + return + + else: + newmodelB = copy.deepcopy(self.model) # self.model.copy() + for i in range(self.h): + # Choose a new replicate + acq_obj.model = copy.deepcopy(newmodelB) # newmodelB.copy() + acq_obj.evaluate(new=False, return_pseudo=True) + acq_obj.model.update(x=acq_obj.znew, Y=acq_obj.fnew, maxit=0) + + if i == 0: + z0 = 1 * acq_obj.znew + path_B = [] + + path_B.append({"par": acq_obj.znew, "new": False}) + newmodelB = copy.deepcopy(acq_obj.model) # acq_obj.model.copy() + + # Choose a new design + acq_obj.evaluate(new=True, return_pseudo=True) + acq_obj.model.update(x=acq_obj.znew, Y=acq_obj.fnew, maxit=0) + path_C = [{"par": acq_obj.znew, "new": True}] + + for j in range(i, self.h - 1): + # Remaining replicates + acq_obj.evaluate(new=False, return_pseudo=True) + acq_obj.model.update(x=acq_obj.znew, Y=acq_obj.fnew, maxit=0) + path_C.append({"par": acq_obj.znew, "new": False}) + + IVAR_C = self.crit_eval(acq_obj.model) + designs.append( + {"input": z0, "path": path_B + path_C, "value": IVAR_C} + ) + + if IVAR_C < IVAR_A: + self.znew, self.new = z0, False + return + + self.znew, self.new = z_A, True + return diff --git a/PUQ/designmethods/gen_funcs/acquisition_funcs.py b/PUQ/designmethods/gen_funcs/acquisition_funcs.py deleted file mode 100644 index 3096f43..0000000 --- a/PUQ/designmethods/gen_funcs/acquisition_funcs.py +++ /dev/null @@ -1,349 +0,0 @@ -"""Contains acquisition functions.""" - -import numpy as np -from sklearn.metrics import pairwise_distances -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - compute_postvar, - compute_eivar, - multiple_pdfs, - get_emuvar, -) -import scipy - - -def rnd( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, -): - theta_acq = prior_func.rnd(n, None) - return theta_acq - - -def maxvar( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, -): - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - theta_acq, f_acq = [], [] - d, p = x.shape[0], theta.shape[1] - obsvar3d = obsvar.reshape(1, d, d) - diags = np.diag(obsvar[real_x, real_x.T]) - d_real = real_x.shape[0] - coef = (2**d_real) * (np.sqrt(np.pi) ** d_real) * np.sqrt(np.prod(diags)) - - # Create a candidate list. - n_clist = 100 * n - clist = prior_func.rnd(n_clist, None) - - # Acquire n thetas. - for i in range(n): - emupredict = emu.predict(x, clist) - emumean = emupredict.mean() - emuvar, is_cov = get_emuvar(emupredict) - emumeanT = emumean.T - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - var_obsvar2 = emuvarT + 0.5 * obsvar3d - postmean = multiple_pdfs( - obs, emumeanT[:, real_x.flatten()], var_obsvar1[:, real_x, real_x.T] - ) - postvar = compute_postvar( - obs, - emumeanT[:, real_x.flatten()], - var_obsvar1[:, real_x, real_x.T], - var_obsvar2[:, real_x, real_x.T], - coef, - ) - - acq_func = postvar.copy() - idc = np.argmax(acq_func) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - f_acq.append(postmean[idc]) - clist = np.delete(clist, idc, 0) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq - - -def eivar( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, -): - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - theta_acq = [] - n_x, p = x.shape[0], theta.shape[1] - obsvar3d = obsvar.reshape(1, n_x, n_x) - - # Create a candidate list - if n == 1: - n_clist = 100 * n - else: - n_clist = 100 * int(n) # 100*int(np.ceil(np.log(n))) - - clist = prior_func.rnd(n_clist, None) - - for i in range(n): - emupredict = emu.predict(x, thetatest) - emumean = emupredict.mean() - emumeanT = emumean.T - emuvar, is_cov = get_emuvar(emupredict) - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - - # Get the n_ref x d x d x n_cand phi matrix - emuphi4d = emu.acquisition(x=x, theta1=thetatest, theta2=clist) - acq_func = [] - - # Pass over all the candidates - for c_id in range(len(clist)): - posteivar = compute_eivar( - var_obsvar1[:, real_x, real_x.T], - emuphi4d[:, real_x, real_x.T, c_id], - emumeanT[:, real_x.flatten()], - emuvar[real_x, :, real_x.T], - obs, - is_cov, - posttest, - ) - acq_func.append(posteivar) - - idc = np.argmin(acq_func) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - clist = np.delete(clist, idc, 0) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq - - -def maxexp( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, -): - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - theta_acq, f_acq = [], [] - d, p = x.shape[0], theta.shape[1] - obsvar3d = obsvar.reshape(1, d, d) - - # Create a candidate list. - n_clist = 100 * n - clist = prior_func.rnd(n_clist, None) - - theta_sd = (theta - thetalimits[:, 0]) / (thetalimits[:, 1] - thetalimits[:, 0]) - theta_cand_sd = (clist - thetalimits[:, 0]) / ( - thetalimits[:, 1] - thetalimits[:, 0] - ) - - for j in range(n): - emupredict = emu.predict(x, clist) - emumean = emupredict.mean() - emuvar, is_cov = get_emuvar(emupredict) - emumeanT = emumean.T - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - postmean = multiple_pdfs( - obs, emumeanT[:, real_x.flatten()], var_obsvar1[:, real_x, real_x.T] - ) - logpostmean = np.log(postmean) - - # Diversity term - d = pairwise_distances(theta_sd, theta_cand_sd, metric="euclidean") - min_dist = np.min(d, axis=0) - - # Acquisition function - acq_func = logpostmean + np.log(min_dist) - - # New theta - idc = np.argmax(acq_func) - theta_sd = np.concatenate((theta_sd, theta_cand_sd[idc][None, :]), axis=0) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - f_acq.append(postmean[idc]) - clist = np.delete(clist, idc, 0) - theta_cand_sd = np.delete(theta_cand_sd, idc, 0) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - - theta_acq = np.array(theta_acq).reshape((n, p)) - return theta_acq - - -def imse( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, -): - # Update emulator for uncompleted jobs. - idnan = np.isnan(fevals).any(axis=0).flatten() - theta_uc = theta[idnan, :] - if sum(idnan) > 0: - fevalshat_uc = emu.predict(x=x, theta=theta_uc) - emu.update(theta=theta_uc, f=fevalshat_uc.mean()) - - theta_acq = [] - n_x, p = x.shape[0], theta.shape[1] - obsvar3d = obsvar.reshape(1, n_x, n_x) - - # Create a candidate list - if n == 1: - n_clist = 100 * n - else: - n_clist = 100 * int(n) # 100*int(np.ceil(np.log(n))) - - clist = prior_func.rnd(n_clist, None) - - for i in range(n): - # Get the n_ref x d x d x n_cand phi matrix - emuphi4d = emu.acquisition(x=x, theta1=thetatest, theta2=clist) - acq_func = [] - - # print(emuphi4d.shape) - # Pass over all the candidates - for c_id in range(len(clist)): - # print(emuphi4d[c_id, :, :, 0]) - posteivar = np.sum(emuphi4d[:, :, :, c_id]) - acq_func.append(posteivar) - - idc = np.argmax(acq_func) - ctheta = clist[idc, :].reshape((1, p)) - theta_acq.append(ctheta) - clist = np.delete(clist, idc, 0) - - # Kriging believer strategy - fevalsnew = emu.predict(x=x, theta=ctheta) - emu.update(theta=ctheta, f=fevalsnew.mean()) - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq - - -def ei( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - thetatest=None, - posttest=None, - type_init=None, -): - n_x, p = x.shape[0], theta.shape[1] - # Create a candidate list - if n == 1: - n_clist = 100 * n - else: - n_clist = 100 * int(n) # 100*int(np.ceil(np.log(n))) - - clist = prior_func.rnd(n_clist, None) - maxpost = np.max(fevals) - - for i in range(n): - emupredict = emu.predict(np.arange(0, 1)[:, None], clist) - emumean = emupredict.mean() - emuvar = emupredict.var() - - zscore = (emumean - maxpost) / np.sqrt(emuvar) - - cdf_cand = scipy.stats.norm.cdf(zscore) - pdf_cand = scipy.stats.norm.pdf(zscore) - ei_cand = (emumean - maxpost) * cdf_cand + np.sqrt(emuvar) * pdf_cand - acqfun = ei_cand - - # print(acqfun) - # print(acqfun) - theta_acq = clist[np.argmax(acqfun), :] - - theta_acq = np.array(theta_acq).reshape((n, p)) - - return theta_acq diff --git a/PUQ/designmethods/gen_funcs/acquisition_md_deterministic.py b/PUQ/designmethods/gen_funcs/acquisition_md_deterministic.py new file mode 100644 index 0000000..b0006c1 --- /dev/null +++ b/PUQ/designmethods/gen_funcs/acquisition_md_deterministic.py @@ -0,0 +1,301 @@ +import numpy as np +from scipy.stats import qmc +from PUQ.designmethods.support import multiple_pdfs, multiple_determinants +from hetgpy.IMSE import crit_IMSPE +import emcee + + +def generate_neighborhood(acq): + N = int(acq.nL) + ndim = acq.tlim.shape[0] + sampler = qmc.LatinHypercube(d=ndim, seed=acq.seed) + unit_sample = sampler.random(n=N) + L = qmc.scale(unit_sample, acq.tlim[:, 0], acq.tlim[:, 1]) + + return L + + +def get_pred(cL, emu, x, ttest, reps): + + cP = emu.predict(x=cL) + var_cand = cP._info["var"] + cP._info["nugs"] / reps + testP = emu.predict(x=ttest, thetaprime=cL) + mu, S, cov = testP._info["mean"], testP._info["S"], testP._info["covmat"] + mut = mu.T + St = np.transpose(S, (2, 0, 1)) + + return mut, St, cov, var_cand + + +class acquisition_function: + def __init__(self, model, cls_func, args): + self.model = model # deepcopy(model) + self.cls_func = cls_func + # self.persis_info = persis_info + self.args = args + self.x = self.cls_func.x + self.d = self.cls_func.d + self.p = self.cls_func.p + self.dx = self.cls_func.dx + self.dt = self.cls_func.dt + + self.tlim = cls_func.thetalimits + self.y = self.cls_func.real_data + self.Sigma = self.cls_func.obsvar + self.Sigma3d = self.Sigma.reshape(1, self.d, self.d) + self.detSigma = np.linalg.det(self.Sigma) + self.twopid = (2**self.d) * (np.sqrt(np.pi) ** self.d) + self.twopiddet = ( + (2**self.d) * (np.sqrt(np.pi) ** self.d) * np.sqrt(self.detSigma) + ) + + def acquire_new(self): + return eval("self.evaluate")(self.args.get("new", True), return_pseudo=False) + + def gen_pred(self, model, x, t, return_id=False, return_flat=False): + + # predict at mesh + meshPr = model.predict(x=t, thetaprime=t) + + # ntot, ntot x ntot, ntot + mu, Sn, sd2 = meshPr._info["mean"], meshPr._info["S"], meshPr._info["var"] + muT = mu.T + S = Sn.transpose(2, 0, 1) + + return muT, S, t + + def crit_pvar(self, model, x, t): + + # posterior variance + mu, S, z = self.gen_pred(model, x, t) + + M = S + 0.5 * self.Sigma3d + N = S + self.Sigma3d + + f = multiple_pdfs(self.y, mu, M) + g = multiple_pdfs(self.y, mu, N) + + vals = (1 / self.twopiddet) * f - g**2 + + return vals + + def total_var(self, model, x, t): + vals = self.crit_pvar(model=model, x=x, t=t) + return np.mean(self.weights * vals) + + +class rnd(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", "both") + self.nL = args.get("nL", 100) + self.t_grid = args.get("t_grid", None) + + def evaluate(self, new, return_pseudo): + + new_input = self.args["prior"].rnd(1, self.args["rand_stream"]) + + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + +class var(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", True) + self.nL = args.get("nL", 100) + + def evaluate(self, new, return_pseudo): + + L = generate_neighborhood(self) + new_input = self.evaluate_explore(L) + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + vals = self.crit_pvar(model=self.model, x=self.x, t=L) + new_input = L[np.argmax(vals), :].reshape(1, self.p) + return new_input + + +class imse(acquisition_function): + # ASK THIS FUNCTION TO DAVID + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", "both") + self.nL = args.get("nL", 100) + self.t_grid = args.get("t_grid", None) + + def evaluate(self, new, return_pseudo): + + sampling = LHS(xlimits=self.tlim, random_state=int(self.seed)) + L = sampling(self.nL) + new_input = self.evaluate_explore(L) + + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + IMSPE_grid = np.array([crit_IMSPE(x, model=self.model) for x in L]) + new_input = L[np.argmin(IMSPE_grid), :].reshape(1, self.cls_func.p) + return new_input + + +class exp(acquisition_function): + # ASK THIS FUNCTION TO DAVID + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", "both") + self.nL = args.get("nL", 100) + self.t_grid = args.get("t_grid", None) + + def evaluate(self, new, return_pseudo): + + sampling = LHS(xlimits=self.tlim, random_state=int(self.seed)) + L = sampling(self.nL) + new_input = self.evaluate_explore(L) + + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + IMSPE_grid = np.array([crit_IMSPE(x, model=self.model) for x in L]) + new_input = L[np.argmin(IMSPE_grid), :].reshape(1, self.cls_func.p) + return new_input + + +class ivar(acquisition_function): + def __init__(self, model, cls_func, args): + super().__init__(model, cls_func, args) + self.seed = args.get("seed", None) + self.explore = args.get("explore", "both") + self.nL = args.get("nL", 100) + if args.get("integral") == "importance": + # print("Importance sampling") + self.epsilon = args.get("epsilon", 10 ** (-10)) + self.discard = args.get("discard", 100) + self.nsteps = args.get("nsteps", 200) + self.thin = args.get("thin", 20) + self.nwalkers = args.get("nwalkers", 20) + self.reference_set() + + elif args.get("integral") == "LHS": + ndim = self.tlim.shape[0] + sampler = qmc.LatinHypercube(d=ndim, seed=int(self.seed)) + unit_sample = sampler.random(n=500) + self.t_ref = qmc.scale(unit_sample, self.tlim[:, 0], self.tlim[:, 1]) + self.weights = 1 + else: + self.t_ref = args.get("t_grid") + self.weights = 1 + + def reference_set(self): + def log_probability(ctheta): + if np.any((ctheta < 0) | (ctheta > 1)): + return -np.inf + else: + pvar = self.crit_pvar(model=self.model, x=self.x, t=ctheta[None, :]) + + pvar = max(pvar, 0) + return np.log(pvar) + + def sample(ndim, nwalkers): + np.random.seed(int(self.seed)) + sampler = emcee.EnsembleSampler(nwalkers, ndim, log_probability) + + sampler_init = qmc.LatinHypercube(d=ndim, seed=int(self.seed)) + unit_sample = sampler_init.random(n=nwalkers) + loc0 = qmc.scale(unit_sample, self.tlim[:, 0], self.tlim[:, 1]) + + sampler.run_mcmc(initial_state=loc0, nsteps=self.nsteps, progress=False) + + samples = sampler.get_chain(discard=self.discard, thin=self.thin, flat=True) + return samples + + def importance_weight(theta): + pvar = self.crit_pvar(model=self.model, x=self.x, t=theta) + pvar = pvar + self.epsilon + unnorm_weight = 1 / pvar + weight = unnorm_weight / np.sum(unnorm_weight) + return weight + + self.t_ref = sample(ndim=self.dt, nwalkers=self.nwalkers) + self.weights = importance_weight(theta=self.t_ref) + + def evaluate(self, new, return_pseudo): + + L = generate_neighborhood(self) + new_input = self.evaluate_explore(L) + self.znew, self.new = new_input, new + + if return_pseudo: + pnew = self.model.predict(x=self.znew) + fnew = np.array([pnew["mean"]])[None, :] + self.fnew = fnew + + return self + + def evaluate_explore(self, L): + + nm, nL = self.t_ref.shape[0], self.nL + vals = np.zeros(nL) + + mu, S, cov, cvar = get_pred( + cL=L, emu=self.model, x=self.x, ttest=self.t_ref, reps=1 + ) + + V1 = S + self.Sigma3d + + q, d = self.model._info["numGPs"], mu.shape[1] + + phi = np.zeros((nm, d, d, nL)) + + coef = 1 / ((2**d) * (np.sqrt(np.pi) ** d)) + + for j in range(0, q): + phi[:, j, j, :] = cov[j, :, :] ** 2 / cvar[j, :] + + for k in range(0, self.nL): + # C1: nm x d x d + # C2: nm x d x d + phic = phi[:, self.x, self.x.T, k] + C1 = (V1 + phic) * 0.5 + C2 = V1 - phic + + rpdf = multiple_pdfs(self.y, mu, C1) + dets = multiple_determinants(C2) + part2 = rpdf / (coef * np.sqrt(dets)) + + vals[k] = np.sum(self.weights * part2) + + new_input = L[np.argmax(vals), :].reshape(1, self.p) + + return new_input diff --git a/PUQ/designmethods/gen_funcs/acquisition_md_stochastic.py b/PUQ/designmethods/gen_funcs/acquisition_md_stochastic.py new file mode 100644 index 0000000..aa20051 --- /dev/null +++ b/PUQ/designmethods/gen_funcs/acquisition_md_stochastic.py @@ -0,0 +1,327 @@ +"""Contains acquisition functions.""" + +import numpy as np +from PUQ.designmethods.gen_funcs.batch_acquisition_funcs_support import ( + build_emulator, + compute_ivar, + impute, + impute_CL, + multiple_determinants, + multiple_pdfs, +) + +# from PUQ.surrogatemethods.pcHetGP import update + + +def get_pred(cL, emu, x, ttest, reps): + + # cP = emu.predict(x=x, theta=cL) + # var_cand = cP._info["var_o"] + cP._info["nugs_o"] / reps + cP = emu.predict(x=cL) + var_cand = cP._info["var"] + cP._info["nugs"] / reps + + # testP = emu.predict(x=x, theta=ttest, thetaprime=cL) + testP = emu.predict(x=ttest, thetaprime=cL) + # mu, S, cov = testP._info["mean"], testP._info["S"], testP._info["cov_o"] + mu, S, cov = testP._info["mean"], testP._info["S"], testP._info["covmat"] + + mut = mu.T + St = np.transpose(S, (2, 0, 1)) + + return mut, St, cov, var_cand + + +def last_iteration(x, obs, obsvar, tE, fE, des_obj): + tmesh, skip, pc_settings = des_obj.tmesh, des_obj.skip, des_obj.pc_settings + if skip: + ivar = 0 + else: + emu = build_emulator(x=x, theta=tE, f=fE, pcset=pc_settings) + ivar = compute_ivar(emu=emu, ttest=tmesh, x=x, obs=obs, obsvar=obsvar) + return ivar + + +def impute_strategy(ct, x, fE, tE, emu, liar, des_obj): + + rep = des_obj.rep + impute_str = des_obj.impute_str + rand_stream = des_obj.rand_stream + pc_settings = des_obj.pc_settings + + if impute_str == "update": + # update(emu._info, x=x, X0new=ct, mult=rep) + Xnew = np.repeat(ct, rep, axis=0) + emu.update(x=Xnew) + fE, tE = impute( + ct=ct, x=x, fE=fE, tE=tE, reps=rep, emu=emu, rnd_str=rand_stream + ) + else: + if impute_str == "KB": + fE, tE = impute( + ct=ct, x=x, fE=fE, tE=tE, reps=rep, emu=emu, rnd_str=rand_stream + ) + elif impute_str == "CL": + fE, tE = impute_CL(ct=ct, x=x, fE=fE, tE=tE, reps=rep, liar=liar) + + emu = build_emulator(x=x, theta=tE, f=fE, pcset=pc_settings) + + return fE, tE, emu + + +class acquire: + def __init__( + self, + bnew, + rep, + emu, + func_cls, + theta_mesh, + prior, + method="simse", + nL=200, + pc_settings={}, + rand_stream=None, + impute_str="KB", + skip=False, + ): + + self.bnew = bnew + self.rep = rep + self.emu = emu + self.x = func_cls.x + self.tmesh = theta_mesh + self.obs = func_cls.real_data + self.obsvar = func_cls.obsvar + self.method = method + self.nL = nL + self.prior = prior + self.pc_settings = pc_settings + self.func_cls = func_cls + self.rand_stream = rand_stream + self.impute_str = "KB" if impute_str is None else impute_str + self.skip = False if skip is None else skip + + # print("Explore rule: ", self.method, " with ", self.impute_str) + + def acquire_new(self): + if self.method == "simse": + self.simse() + elif self.method == "seivar": + self.seivar() + elif self.method == "var": + self.var() + + return + + def seivar(self): + + x = self.x + obs = self.obs + obsvar = self.obsvar + n_x = x.shape[0] + # 1 x d x d + obsvar3d = obsvar.reshape(1, n_x, n_x) + + nm = self.tmesh.shape[0] + p = self.tmesh.shape[1] + + # Create a candidate list + nL = self.nL + cL = self.prior.rnd(self.nL, self.rand_stream) + + # Get emulator + emu = self.emu + # cov : q x nm x nL + # cvar : q x nL + # mu : nm x d + # S : nm x d x d + mu, S, cov, cvar = get_pred( + cL=cL, emu=emu, x=x, ttest=self.tmesh, reps=self.rep + ) + V1 = S + obsvar3d + + fE = emu._info["f"] + tE = emu._info["theta"] + + liar = np.mean(fE, axis=1) + + tnew = [] + + q, d = self.emu._info["numGPs"], mu.shape[1] + for i in range(self.bnew): + # G = emu._info["G"] + # B = emu._info["B"] + # GB = G @ B + # BTG = B.T @ G + + # d = G.shape[0] + # q = B.shape[1] + + # tau = np.zeros((nm, q, q, nL)) + phi = np.zeros((nm, d, d, nL)) + + coef = 1 / ((2**d) * (np.sqrt(np.pi) ** d)) + + for j in range(0, q): + phi[:, j, j, :] = cov[j, :, :] ** 2 / cvar[j, :] + + # for j in range(0, q): + # tau[:, j, j, :] = cov[j, :, :] ** 2 / cvar[j, :] + + # for k in range(0, nL): + # phi[:, :, :, k] = GB @ tau[:, :, :, k] @ BTG + + vals = [] + for k in range(0, nL): + # C1: nm x d x d + # C2: nm x d x d + phic = phi[:, x, x.T, k] + C1 = (V1 + phic) * 0.5 + C2 = V1 - phic + + rpdf = multiple_pdfs(obs, mu, C1) + dets = multiple_determinants(C2) + part2 = rpdf / (coef * np.sqrt(dets)) + + cval = -np.sum(part2) + vals.append(cval) + + idc = np.argmin(vals) + ct = cL[idc, :].reshape((1, p)) + tnew.append(ct) + + fE, tE, emu = impute_strategy(ct, x, fE, tE, emu, liar, self) + + cL = self.prior.rnd(nL, self.rand_stream) + + mu, S, cov, cvar = get_pred( + cL=cL, emu=emu, x=x, ttest=self.tmesh, reps=self.rep + ) + V1 = S + obsvar3d + + tnew = np.array(tnew).reshape((self.bnew, p)) + ivar = last_iteration(x, obs, obsvar, tE, fE, self) + self.tnew = tnew + self.ivar = ivar + return + + def simse(self): + + x = self.x + obs = self.obs + obsvar = self.obsvar + n_x = x.shape[0] + obsvar3d = obsvar.reshape(1, n_x, n_x) + + nm = self.tmesh.shape[0] + p = self.tmesh.shape[1] + + # Create a candidate list + nL = self.nL + cL = self.prior.rnd(self.nL, self.rand_stream) + + # Get emulator + emu = self.emu + + # cov : q x nm x nL + # cvar : q x nL + _, _, cov, cvar = get_pred(cL=cL, emu=emu, x=x, ttest=self.tmesh, reps=self.rep) + fE = emu._info["f"] + tE = emu._info["theta"] + + liar = np.mean(fE, axis=1) + + tnew = [] + for i in range(self.bnew): + G = emu._info["G"] + B = emu._info["B"] + GB = G @ B + BTG = B.T @ G + + d = G.shape[0] + q = B.shape[1] + + tau = np.zeros((nm, q, q, nL)) + phi = np.zeros((nm, d, d, nL)) + + for j in range(0, q): + tau[:, j, j, :] = cov[j, :, :] ** 2 / cvar[j, :] + + for k in range(0, nL): + phi[:, :, :, k] = GB @ tau[:, :, :, k] @ BTG + + phidiag = np.diagonal(phi, axis1=1, axis2=2) + + vals = [] + for k in range(0, nL): + cval = -np.sum(phidiag[:, k, :]) + vals.append(cval) + + idc = np.argmin(vals) + ct = cL[idc, :].reshape((1, p)) + tnew.append(ct) + + fE, tE, emu = impute_strategy(ct, x, fE, tE, emu, liar, self) + + cL = self.prior.rnd(nL, self.rand_stream) + _, _, cov, cvar = get_pred( + cL=cL, emu=emu, x=x, ttest=self.tmesh, reps=self.rep + ) + tnew = np.array(tnew).reshape((self.bnew, p)) + ivar = last_iteration(x, obs, obsvar, tE, fE, self) + self.tnew = tnew + self.ivar = ivar + return + + def var(self): + x = self.x + obs = self.obs + obsvar = self.obsvar + n_x = x.shape[0] + # 1 x d x d + obsvar3d = obsvar.reshape(1, n_x, n_x) + + p = self.tmesh.shape[1] + + # Create a candidate list + nL = self.nL + cL = self.prior.rnd(self.nL, self.rand_stream) + + # Get emulator + emu = self.emu + + fE = emu._info["f"] + tE = emu._info["theta"] + + liar = np.mean(fE, axis=1) + + tnew = [] + det = np.linalg.det(obsvar) + + for i in range(self.bnew): + pcL = emu.predict(x=x, theta=cL) + mu, S = pcL._info["mean"].T, pcL._info["S"] + St = np.transpose(S, (2, 0, 1)) + + M = St + 0.5 * obsvar3d + N = St + obsvar3d + f = multiple_pdfs(obs, mu, M) + g = multiple_pdfs(obs, mu, N) + + coef = 1 / ((2**n_x) * (np.sqrt(np.pi) ** n_x) * np.sqrt(det)) + + vals = coef * f - g**2 + + idc = np.argmax(vals) + ct = cL[idc, :].reshape((1, p)) + tnew.append(ct) + + fE, tE, emu = impute_strategy(ct, x, fE, tE, emu, liar, self) + + cL = self.prior.rnd(nL, self.rand_stream) + + tnew = np.array(tnew).reshape((self.bnew, p)) + ivar = last_iteration(x, obs, obsvar, tE, fE, self) + self.tnew = tnew + self.ivar = ivar + return diff --git a/PUQ/designmethods/gen_funcs/allocate_reps_1d.py b/PUQ/designmethods/gen_funcs/allocate_reps_1d.py new file mode 100644 index 0000000..16ad40f --- /dev/null +++ b/PUQ/designmethods/gen_funcs/allocate_reps_1d.py @@ -0,0 +1,208 @@ +import numpy as np +import scipy +from numpy.linalg import cholesky, inv, det +from hetgpy.covariance_functions import cov_gen +from PUQ.designmethods.support import multiple_pdfs + + +class allocate: + def __init__( + self, + budget, + model, + func_cls, + mesh, + trace=1, + alloc_settings={}, + rand_stream=None, + ): + + self.budget = budget + self.model = model + self.x = func_cls.x + self.theta_mesh = mesh["theta"] + self.w_mesh = mesh["weight"] + self.obs = func_cls.real_data + self.obsvar = func_cls.obsvar + self.use_Ki = alloc_settings.get("use_Ki", True) + self.eps = np.sqrt(np.finfo(float).eps) + self.trace = trace + self.func_cls = func_cls + self.rand_stream = rand_stream + self.z = self.model._info["X0"] # self.model['X0'] + self.a0 = self.model._info["mult"] # self.model['mult'] + + def allocatereps(self): + + Cinfo = self.compute_ivar_weights() + C = Cinfo.flatten() + + idneg = C < 0 + weight = np.sqrt(np.abs(C[idneg])) + a_frac = np.zeros(len(C)) + a_frac_ub = np.zeros(len(C)) + total_a = np.sum(self.a0) + self.budget + + # Find an upper bound + a_frac_ub[idneg] = total_a * weight / sum(weight) + a_ub = self.make_int(total_a, a_frac_ub) + a_ub = np.maximum(0, a_ub - self.a0) + + a_frac[idneg] = self.budget * a_ub[idneg] / sum(a_ub) + a_final = self.make_int(self.budget, a_frac) + a_final = np.array([int(a) for a in a_final]) + if np.sum(a_final) != self.budget: + raise ValueError("Budget constraint is not satisfied.") + + self.reps = a_final + return + + def compute_ivar_weights(self): + + d = len(self.x) + n_integ = self.theta_mesh.shape[0] + n = self.z.shape[0] + + # pred_nugs = self.emu.predict(x=self.x, theta=self.theta, thetaprime=None) + # pred_mesh = self.model.predict(x=self.theta_mesh) + + # obsvar3d = self.obsvar.reshape(1, d, d) + # # ntest x d + # mu = pred_mesh._info['mean'].T + # S = pred_mesh._info['S'] + # St = np.transpose(S, (2, 0, 1)) + + obsvar3d = self.obsvar.reshape(1, d, d) + + x = self.x + ntot = n_integ * d + t = self.theta_mesh + x_tiled = np.tile(x, (t.shape[0], 1)) + t_repeated = np.repeat(t, x.shape[0], axis=0) + z = np.hstack([x_tiled, t_repeated]) + + # to construct S matrix + id_row = np.arange(0, ntot) + id_col = np.arange(0, ntot).reshape(n_integ, d) + id_col = np.repeat(id_col, repeats=d, axis=0) + + # predict at mesh + # meshPr = self.model.predict(x=z, xprime=z) + meshPr = self.model.predict(x=z, thetaprime=z) + + # ntot, ntot x ntot, ntot + # mu, Sn = meshPr["mean"], meshPr["cov"] + mu, Sn = meshPr._info["mean"], meshPr._info["covmat"] + + muT = mu.reshape(n_integ, d) + S = Sn[id_row[:, None], id_col].reshape(n_integ, d, d) + + # ntest x d x d + Nb = S + 0.5 * obsvar3d + N = S + obsvar3d + + f = multiple_pdfs(self.obs, muT, Nb) + g = multiple_pdfs(self.obs, muT, N) + + # ntest x 1 x d + h = (self.obs - muT).reshape(n_integ, 1, d) + + # ntest x d x d + Nbinv = inv(Nb) + + # ntest x d x d + Ninv = inv(N) + + # ntest x 1 x d + hNb = np.matmul(h, Nbinv) + hN = np.matmul(h, Ninv) + + coef = 1 / ((2**d) * (np.sqrt(np.pi) ** d) * np.sqrt(det(self.obsvar))) + + Mb = np.zeros((n, n_integ, d, d), dtype=np.float64) + + pred_nugs = self.model.predict( + x=self.z, args=dict(nugs_only=True) + ) # nugs_only=True) + # n x n + if self.use_Ki: + # Ki = self.model['Ki'] + Ki = self.model._info["Ki"] + else: + # K = cov_gen(X1=self.z, theta=self.model['theta'], type = self.model['covtype']) + K = cov_gen( + X1=self.z, + theta=self.model._info["theta"], + type=self.model._info["covtype"], + ) + Ki = scipy.linalg.pinv(K, rcond=self.eps) + + ids = np.arange(0, len(z), d) + for i in range(0, n): + J = np.zeros((n, n)) + J[i, i] = 1 + + # n x n + A = Ki @ J @ Ki + + for j in range(0, d): + # n_integ x 1 + zo = z[ids + j] + + # n x n_integ + # kv = cov_gen(X1=self.z, X2=zo, theta=self.model['theta'], type = self.model['covtype']) + kv = cov_gen( + X1=self.z, + X2=zo, + theta=self.model._info["theta"], + type=self.model._info["covtype"], + ) + + for jp in range(0, d): + # n_integ x 1 + zop = z[ids + jp] + + # n x n_integ + # kvp = cov_gen(X1=self.z, X2=zop, theta=self.model['theta'], type = self.model['covtype']) + kvp = cov_gen( + X1=self.z, + X2=zop, + theta=self.model._info["theta"], + type=self.model._info["covtype"], + ) + # Mb[i, :, j, jp] = -pred_nugs["nugs"][i]*np.einsum('ji,jk,ki->i', kv, A, kvp) + Mb[i, :, j, jp] = -pred_nugs._info["nugs"][i] * np.einsum( + "ji,jk,ki->i", kv, A, kvp + ) + + dlogfdai = np.zeros((n, n_integ)) + dloggdai = np.zeros((n, n_integ)) + C = np.zeros((n, 1)) + for i in range(0, n): + + M = Mb[i, :, :, :] + + part1f = -0.5 * np.trace(np.matmul(Nbinv, M), axis1=1, axis2=2) + part2f = 0.5 * (np.matmul(np.matmul(hNb, M), np.transpose(hNb, (0, 2, 1)))) + part1g = -0.5 * np.trace(np.matmul(Ninv, M), axis1=1, axis2=2) + part2g = 0.5 * (np.matmul(np.matmul(hN, M), np.transpose(hN, (0, 2, 1)))) + + dlogfdai = part1f + part2f.flatten() + dloggdai = part1g + part2g.flatten() + subC = self.w_mesh * (coef * (f * dlogfdai) - 2 * (g**2) * dloggdai) + + C[i] = np.sum(subC) + + return C + + def make_int(self, bdg, n_frac): + # Make integer + n_floor = np.floor(n_frac) + remain = n_frac - n_floor + tot_remain = bdg - np.sum(n_floor) + sorted_indices = np.array(np.argsort(remain)[::-1]) + if tot_remain > 0: + idx = sorted_indices[np.arange(0, tot_remain, dtype=int)] + n_floor[idx] = n_floor[idx] + 1 + + return n_floor diff --git a/PUQ/designmethods/gen_funcs/allocate_reps_md.py b/PUQ/designmethods/gen_funcs/allocate_reps_md.py new file mode 100644 index 0000000..44fd145 --- /dev/null +++ b/PUQ/designmethods/gen_funcs/allocate_reps_md.py @@ -0,0 +1,313 @@ +import numpy as np +import scipy +from numpy.linalg import cholesky, inv, det +from hetgpy.covariance_functions import cov_gen +from PUQ.designmethods.gen_funcs.batch_acquisition_funcs_support import ( + multiple_pdfs, + build_emulator, + impute, + compute_ivar, +) + + +class allocate: + def __init__( + self, + budget, + emu_info, + func_cls, + theta_mesh, + prior, + method="imse", + trace=1, + alloc_settings={}, + rand_stream=None, + ): + + self.budget = budget + self.emu = emu_info + self.x = func_cls.x + self.theta_mesh = theta_mesh + self.obs = func_cls.real_data + self.obsvar = func_cls.obsvar + self.method = method + self.use_Ki = alloc_settings.get("use_Ki") + self.eps = np.sqrt(np.finfo(float).eps) + self.trace = trace + self.func_cls = func_cls + self.rand_stream = rand_stream + + if alloc_settings.get("theta") is None: + if alloc_settings.get("gen") is False: + # self.theta = self.emu._info['emulist'][0]['X0'] + self.theta = self.emu._info["emulist"][0]._info["X0"] + else: + self.theta = prior.rnd(100, None) + self.theta = np.concatenate( + (self.emu._info["emulist"][0]["X0"], self.theta), axis=0 + ) + else: + self.theta = alloc_settings["theta"] + + if alloc_settings.get("a0") is None: + if alloc_settings.get("gen") is False: + # self.a0 = self.emu._info['emulist'][0]['mult'] + self.a0 = self.emu._info["emulist"][0]._info["mult"] + else: + self.a0 = np.concatenate( + (self.emu["emulist"]._info[0]["mult"], np.repeat(0, 100)) + ) + else: + self.a0 = alloc_settings["a0"] + + def allocatereps(self): + + if self.trace == 1: + print("Allocation rule: ", self.method, " with ", self.emu._info["method"]) + + if self.method == "imse": + Cinfo = self.compute_imse_weights() + C = Cinfo.flatten() + elif self.method == "ivar": + Cinfo = self.compute_ivar_weights() + C = Cinfo.flatten() + elif self.method == "var": + Cinfo = self.compute_var_weights() + C = Cinfo.flatten() + + idneg = C < 0 + weight = np.sqrt(np.abs(C[idneg])) + a_frac = np.zeros(len(C)) + a_frac_ub = np.zeros(len(C)) + total_a = np.sum(self.a0) + self.budget + + # Find an upper bound + a_frac_ub[idneg] = total_a * weight / sum(weight) + a_ub = self.make_int(total_a, a_frac_ub) + a_ub = np.maximum(0, a_ub - self.a0) + + a_frac[idneg] = self.budget * a_ub[idneg] / sum(a_ub) + a_final = self.make_int(self.budget, a_frac) + a_final = np.array([int(a) for a in a_final]) + if np.sum(a_final) != self.budget: + raise ValueError("Budget constraint is not satisfied.") + + self.reps = a_final + return + + def compute_imse_weights(self): + + d = len(self.x) + n_integ = self.theta_mesh.shape[0] + n = self.theta.shape[0] + + pred_nugs = self.emu.predict(x=self.x, theta=self.theta, thetaprime=None) + + # d x d + G = self.emu._info["G"] + # d x q + B = self.emu._info["B"] + # d x q + GB = G @ B + BTG = B.T @ G + + q = len(self.emu._info["emulist"]) + Mb = np.zeros((n, n_integ, q, q)) + for i in range(0, n): + J = np.zeros((n, n)) + J[i, i] = 1 + for j in range(0, q): + emuinfo = self.emu._info["emulist"][j] + K_s = cov_gen(X1=self.theta, X2=self.theta_mesh, theta=emuinfo["theta"]) + + if self.use_Ki: + Ki = emuinfo["Ki"] + else: + K = cov_gen(X1=self.theta, theta=emuinfo["theta"]) + Ki = scipy.linalg.pinv(K, rcond=self.eps) + # Ki = scipy.linalg.pinv2(K, rcond=self.eps) + + A = Ki @ J @ Ki + if emuinfo["is_homGP"]: + Mb[i, :, j, j] = ( + -emuinfo["nu_hat"] + * emuinfo["g"] + * np.einsum("ji,jk,ki->i", K_s, A, K_s) + ) + else: + Mb[i, :, j, j] = -(pred_nugs._info["nugs_o"][j, i]) * np.einsum( + "ji,jk,ki->i", K_s, A, K_s + ) + + C = np.zeros((n, 1)) + for i in range(0, n): + for l in range(0, n_integ): + par = GB @ Mb[i, l, :, :] @ BTG + C[i] += np.sum(np.diag(par)) + + return C + + def compute_ivar_weights(self): + + d = len(self.x) + n_integ = self.theta_mesh.shape[0] + n = self.theta.shape[0] + + # pred_nugs = self.emu.predict(x=self.x, theta=self.theta, thetaprime=None) + # pred_mesh = self.emu.predict(x=self.x, theta=self.theta_mesh, thetaprime=None) + + pred_nugs = self.emu.predict(x=self.theta, thetaprime=None) + pred_mesh = self.emu.predict(x=self.theta_mesh, thetaprime=None) + + obsvar3d = self.obsvar.reshape(1, d, d) + # ntest x d + mu = pred_mesh._info["mean"].T + S = pred_mesh._info["S"] + St = np.transpose(S, (2, 0, 1)) + + # ntest x d x d + Nb = St + 0.5 * obsvar3d + N = St + obsvar3d + + f = multiple_pdfs(self.obs, mu, Nb) + g = multiple_pdfs(self.obs, mu, N) + + # ntest x 1 x d + h = (self.obs - mu).reshape(n_integ, 1, d) + + # ntest x d x d + Nbinv = inv(Nb) + + # ntest x d x d + Ninv = inv(N) + + # ntest x 1 x d + hNb = np.matmul(h, Nbinv) + hN = np.matmul(h, Ninv) + + # # d x d + # G = self.emu._info['G'] + # # d x q + # B = self.emu._info['B'] + # # d x q + # GB = G @ B + # BTG = B.T @ G + + coef = 1 / ((2**d) * (np.sqrt(np.pi) ** d) * np.sqrt(det(self.obsvar))) + + q = len(self.emu._info["emulist"]) + COVTYPE = self.emu._info["emulist"][0]._info["covtype"] + Mb = np.zeros((n, n_integ, q, q)) + + for i in range(0, n): + J = np.zeros((n, n)) + J[i, i] = 1 + for j in range(0, q): + # emuinfo = self.emu._info['emulist'][j] + emuinfo = self.emu._info["emulist"][j]._info + K_s = cov_gen( + X1=self.theta, + X2=self.theta_mesh, + theta=emuinfo["theta"], + type=COVTYPE, + ) + + if self.use_Ki: + Ki = emuinfo["Ki"] + else: + K = cov_gen(X1=self.theta, theta=emuinfo["theta"], type=COVTYPE) + Ki = scipy.linalg.pinv(K, rcond=self.eps) + + A = Ki @ J @ Ki + if emuinfo["is_homGP"]: + Mb[i, :, j, j] = -(emuinfo["g"] * emuinfo["nu_hat"]) * np.einsum( + "ji,jk,ki->i", K_s, A, K_s + ) + else: + # Mb[i, :, j, j] = -(pred_nugs._info['nugs_o'][j, i])*np.einsum('ji,jk,ki->i', K_s, A, K_s) + Mb[i, :, j, j] = -(pred_nugs._info["nugs"][j, i]) * np.einsum( + "ji,jk,ki->i", K_s, A, K_s + ) + + dlogfdai = np.zeros((n, n_integ)) + dloggdai = np.zeros((n, n_integ)) + C = np.zeros((n, 1)) + for i in range(0, n): + + # M = GB @ Mb[i, :, :, :] @ BTG + M = Mb[i, :, :, :] + part1f = -0.5 * np.trace(np.matmul(Nbinv, M), axis1=1, axis2=2) + part2f = 0.5 * (np.matmul(np.matmul(hNb, M), np.transpose(hNb, (0, 2, 1)))) + part1g = -0.5 * np.trace(np.matmul(Ninv, M), axis1=1, axis2=2) + part2g = 0.5 * (np.matmul(np.matmul(hN, M), np.transpose(hN, (0, 2, 1)))) + + dlogfdai = part1f + part2f.flatten() + dloggdai = part1g + part2g.flatten() + C[i] = np.sum(coef * (f * dlogfdai) - 2 * (g**2) * dloggdai) + + return C + + def compute_var_weights(self): + + d = len(self.x) + pred_nugs = self.emu.predict(x=self.x, theta=self.theta, thetaprime=None) + + obsvar3d = self.obsvar.reshape(1, d, d) + + # ntest x d + mu = pred_nugs._info["mean"].T + S = pred_nugs._info["S"] + St = np.transpose(S, (2, 0, 1)) + + # ntest x d x d + Nb = St + 0.5 * obsvar3d + N = St + obsvar3d + + f = multiple_pdfs(self.obs, mu, Nb) + g = multiple_pdfs(self.obs, mu, N) + + coef = 1 / ((2**d) * (np.sqrt(np.pi) ** d) * np.sqrt(det(self.obsvar))) + + var = coef * f - g**2 + C = -1 * var + + return C + + def make_int(self, bdg, n_frac): + # Make integer + n_floor = np.floor(n_frac) + remain = n_frac - n_floor + tot_remain = bdg - np.sum(n_floor) + sorted_indices = np.array(np.argsort(remain)[::-1]) + if tot_remain > 0: + idx = sorted_indices[np.arange(0, tot_remain, dtype=int)] + n_floor[idx] = n_floor[idx] + 1 + + return n_floor + + def ivar_exploit(self, emu, pc_settings): + texploit = self.theta + rep = self.reps + x = self.x + tmesh = self.theta_mesh + obs = self.obs + obsvar = self.obsvar + + fE = emu._info["f"] + tE = emu._info["theta"] + for cid, ct in enumerate(texploit): + if rep[cid] > 0: + fE, tE = impute( + ct=ct[None, :], + x=x, + fE=fE, + tE=tE, + reps=rep[cid], + emu=emu, + rnd_str=self.rand_stream, + ) + + emu = build_emulator(x=x, theta=tE, f=fE, pcset=pc_settings) + ivar = compute_ivar(emu=emu, ttest=tmesh, x=x, obs=obs, obsvar=obsvar) + + return ivar diff --git a/PUQ/designmethods/gen_funcs/batch_acquisition_funcs_support.py b/PUQ/designmethods/gen_funcs/batch_acquisition_funcs_support.py new file mode 100644 index 0000000..622b2ee --- /dev/null +++ b/PUQ/designmethods/gen_funcs/batch_acquisition_funcs_support.py @@ -0,0 +1,161 @@ +"""Contains supplemental methods for acquisitionn funcs.""" + +import numpy as np +from PUQ.surrogate import emulator +from numpy.linalg import inv, det + + +def impute(ct, x, fE, tE, reps, emu, rnd_str): + + # NEW PART + if fE.shape[0] > 1: + # cpred = emu.predict(x=x, theta=ct) + cpred = emu.predict(x=ct) + cm = cpred.mean() + cS = cpred._info["S"] + cR = cpred._info["R"] + fnoise = rnd_str.multivariate_normal( + mean=cm.flatten(), cov=cS[:, :, 0] + cR[:, :, 0], size=reps + ) + + tE = np.concatenate([tE, np.repeat(ct, reps, axis=0)]) + fE = np.concatenate([fE, fnoise.T], axis=1) + else: + # cpred = emu.predict(x=x, theta=ct) + cpred = emu.predict(x=ct) + cm = cpred.mean() + # cv = cpred._info['var_noisy'] + cv = cpred._info["var"] + cpred._info["nugs"] + fnoise = rnd_str.normal( + loc=cm.flatten(), scale=np.sqrt(cv.flatten()), size=reps + ) + + tE = np.concatenate([tE, np.repeat(ct, reps, axis=0)]) + fE = np.concatenate([fE, fnoise.reshape(1, reps)], axis=1) + + return fE, tE + + +def impute_CL(ct, x, fE, tE, reps, liar): + + # print(fE) + d = fE.shape[0] + + tE = np.concatenate([tE, np.repeat(ct, reps, axis=0)]) + fE = np.concatenate([fE, np.repeat(liar, reps).reshape(d, reps)], axis=1) + + # print(fE) + + return fE, tE + + +def build_emulator(x, theta, f, pcset): + + # emu = emulator(x=x, + # theta=theta, + # f=f, + # method="pcHetGP", + # args={'lower':None, 'upper':None, + # 'noiseControl':{'k_theta_g_bounds': (1, 100), 'g_max': 1e2, 'g_bounds': (1e-6, 1)}, + # 'init':{}, + # 'known':{}, + # 'settings':{"linkThetas": 'joint', "logN": True, "initStrategy": 'residuals', + # "checkHom": True, "penalty": True, "trace": 0, "return.matrices": True, + # "return.hom": False, "factr": 1e9}, + # 'pc_settings':pcset}) + + d = f.shape[0] + md = np.arange(d).reshape(d, 1) + emu = emulator( + x=theta, + theta=md, + f=f, + method="multihetGP", + args={ + "lower": None, + "upper": None, + "noiseControl": { + "k_theta_g_bounds": (1, 100), + "g_max": 1e2, + "g_bounds": (1e-6, 1), + }, + "init": {}, + "known": {}, + "settings": { + "linkThetas": "joint", + "logN": True, + "initStrategy": "residuals", + "checkHom": True, + "penalty": True, + "trace": 0, + "return.matrices": True, + "return.hom": False, + "factr": 1e9, + }, + "pc_settings": pcset, + }, + ) + + return emu + + +def compute_ivar(emu, ttest, x, obs, obsvar): + + # testP = emu.predict(x=x, theta=ttest) + testP = emu.predict(x=ttest) + d = len(x) + obsvar3d = obsvar.reshape(1, d, d) + + # ntest x d + mu = testP._info["mean"].T + S = testP._info["S"] + St = np.transpose(S, (2, 0, 1)) + + # ntest x d x d + M = St + 0.5 * obsvar3d + N = St + obsvar3d + + f = multiple_pdfs(obs, mu, M) + g = multiple_pdfs(obs, mu, N) + + coef = 1 / ((2**d) * (np.sqrt(np.pi) ** d) * np.sqrt(det(obsvar))) + + # ivar compute + ivar = np.sum(coef * f - g**2) + return ivar + + +def multiple_pdfs(x, means, covs): + # Cite: http://gregorygundersen.com/blog/2020/12/12/group-multivariate-normal-pdf/ + + # NumPy broadcasts `eigh`. + vals, vecs = np.linalg.eigh(covs) + + # Compute the log determinants across the second axis. + logdets = np.sum(np.log(vals), axis=1) + + # Invert the eigenvalues. + valsinvs = 1.0 / vals + + # Add a dimension to `valsinvs` so that NumPy broadcasts appropriately. + Us = vecs * np.sqrt(valsinvs)[:, None] + devs = x - means + + # Use `einsum` for matrix-vector multiplications across the first dimension. + devUs = np.einsum("ni,nij->nj", devs, Us) + + # Compute the Mahalanobis distance by squaring each term and summing. + mahas = np.sum(np.square(devUs), axis=1) + + # Compute and broadcast scalar normalizers. + dim = len(vals[0]) + log2pi = np.log(2 * np.pi) + return np.exp(-0.5 * (dim * log2pi + mahas + logdets)) + + +def multiple_determinants(covs): + vals, vecs = np.linalg.eigh(covs) + # Compute the log determinants across the second axis. + # dets = np.prod(vals, axis=1) + logdets = np.sum(np.log(vals), axis=1) + return np.exp(logdets) # dets#np.exp(logdets) diff --git a/PUQ/designmethods/sequential_1d_deterministic.py b/PUQ/designmethods/sequential_1d_deterministic.py new file mode 100644 index 0000000..e836a3a --- /dev/null +++ b/PUQ/designmethods/sequential_1d_deterministic.py @@ -0,0 +1,190 @@ +import numpy as np +from PUQ.designmethods.support import multiple_pdfs, multiple_determinants +from PUQ.designmethods.gen_funcs.acquisition_1d_deterministic import var, ivar, imse +import time +from PUQ.surrogate import emulator + + +class sequential_design: + def __init__(self, cls_func, trace=False): + self.cls_func = cls_func + self.trace = trace + self.y = self.cls_func.real_data + self.d = self.cls_func.d + self.Sigma = self.cls_func.obsvar + self.Sigma3d = self.Sigma.reshape(1, self.d, self.d) + self.detSigma = np.linalg.det(self.Sigma) + return + + def __getitem__(self, key): + return self.__dict__[key] + + def __setitem__(self, item, value): + self.__dict__[item] = value + + def get(self, key): + return self.__dict__.get(key) + + def build_design(self, z0, f0, T, af, test=None, args={}): + + # if test is not None: + # self.test_data_gen(test) + + H = [] + timel = [] + metric_sum = {} + for t in range(0, T): + print(f"t: {t}") if self.trace else None + + tic = time.time() + model = emulator( + x=z0, + theta=np.array([0]), + f=f0, + method="homGP", + args={"known": {"beta0": np.mean(f0)}}, + ) + model.fit() + + toc = time.time() + timel.append(toc - tic) + + args["seed"] += 1 + + # if test is not None: + # metric_sum = self.eval_perf(model, args.get("extra_metric", True)) + + acq_func = eval(af)(model=model, cls_func=self.cls_func, args=args) + + acq_func.acquire_new() + znew = acq_func.znew + fnew = self.cls_func.function(znew.flatten()[0], znew.flatten()[1]).reshape( + 1, 1 + ) + + f0 = np.concatenate((f0, fnew), axis=0) + z0 = np.concatenate((z0, znew), axis=0) + + H.append( + { + "t": t, + "new": acq_func.new, + "h": getattr(acq_func, "h", None), + "f": fnew, + "z": znew, + "MSE": metric_sum.get("MSE", None), + "MAD": metric_sum.get("MAD", None), + "VAR": metric_sum.get("VAR", None), + "MSEy": metric_sum.get("MSEy", None), + "MADy": metric_sum.get("MADy", None), + "MSEn": metric_sum.get("MSEn", None), + "MADn": metric_sum.get("MADn", None), + "summary_metric": metric_sum.get("sum_metric", None), + } + ) + + unique_rows, counts = np.unique(z0, axis=0, return_counts=True) + + self.xt = unique_rows + self.reps = counts + self.fs = f0 + self.zs = z0 + self.time = timel + self.H = H + + return self + + def test_data_gen(self, test): + + # Gen test data + self.ttest, self.ptest, self.wtest, self.ntest, self.ftest = ( + test["theta"], + test["p"], + test["w"], + test["noise"], + test["f"], + ) + # plt.scatter(self.ttest[:, 0], self.ttest[:, 1], c=self.ftest) + # plt.show() + + # plt.scatter(self.ttest[:, 0], self.ttest[:, 1], c=self.ntest) + # plt.show() + + x_tiled = np.tile(self.cls_func.x, (self.ttest.shape[0], 1)) + t_repeated = np.repeat(self.ttest, self.cls_func.x.shape[0], axis=0) + self.ztest = np.hstack([x_tiled, t_repeated]) + + # To be used for performance evaluations + ntot, nm, d = self.ztest.shape[0], self.ttest.shape[0], self.cls_func.d + + # to construct S matrix + self.idr = np.arange(0, ntot)[:, None] + idc = np.arange(0, ntot).reshape(nm, d) + self.idc = np.repeat(idc, repeats=d, axis=0) + + def eval_perf(self, model, extra_metric=False): + + nm, d = self.ttest.shape[0], self.cls_func.d + # sc = plt.scatter(self.ztest[:, 1], self.ztest[:, 2], c=self.wtest, cmap="viridis") # Use any colormap + # plt.colorbar(sc, label="wtest values") # Add a colorbar + # plt.show() + + # predict at mesh + pr = model.predict(x=self.ztest, thetaprime=self.ztest) + + # ntot, ntot x ntot, ntot + # mu, Sn, nugs = pr["mean"], pr["cov"], pr["nugs"] + mu, Sn, nugs = pr._info["mean"], pr._info["covmat"], pr._info["nugs"] + + muT = mu.reshape(nm, d) + S = Sn[self.idr, self.idc].reshape(nm, d, d) + + N = S + self.Sigma3d + g = multiple_pdfs(self.y, muT, N) + + MSE = np.mean(((g.flatten() - self.ptest.flatten()) ** 2) * self.wtest) + MAD = np.mean(np.abs(g.flatten() - self.ptest.flatten()) * self.wtest) + + M = S + 0.5 * self.Sigma3d + f = multiple_pdfs(self.y, muT, M) + + twopiddet = (2**self.d) * (np.sqrt(np.pi) ** self.d) * np.sqrt(self.detSigma) + VAR = np.mean(((1 / twopiddet) * f - g**2) * self.wtest) + + if extra_metric: + diff_y = (mu.flatten() - self.ftest.flatten()).reshape(nm, d) + diff_n = (nugs.flatten() - self.ntest.flatten()).reshape(nm, d) + + MSEy = np.mean(np.mean(diff_y**2, axis=1).flatten() * self.wtest) + MADy = np.mean(np.mean(np.abs(diff_y), axis=1).flatten() * self.wtest) + + MSEn = np.mean(np.mean(diff_n**2, axis=1).flatten() * self.wtest) + MADn = np.mean(np.mean(np.abs(diff_n), axis=1).flatten() * self.wtest) + + # Weighted absolute differences + f_w = np.mean(np.abs(diff_y) * self.wtest[:, None], axis=0) + n_w = np.mean(np.abs(diff_n) * self.wtest[:, None], axis=0) + + # Unweighted absolute differences + f_wh = np.mean(np.abs(diff_y), axis=0) + n_wh = np.mean(np.abs(diff_n), axis=0) + summary = {} + for di in range(d): + summary[f"f{di+1}w"] = f_w[di] + summary[f"f{di+1}"] = f_wh[di] + summary[f"n{di+1}w"] = n_w[di] + summary[f"n{di+1}"] = n_wh[di] + else: + MSEy, MADy, MSEn, MADn, summary = 0, 0, 0, 0, 0 + + metric_sum = { + "MSE": MSE, + "MAD": MAD, + "VAR": VAR, + "MSEy": MSEy, + "MADy": MADy, + "MSEn": MSEn, + "MADn": MADn, + "summary": summary, + } + return metric_sum diff --git a/PUQ/designmethods/sequential_1d_stochastic.py b/PUQ/designmethods/sequential_1d_stochastic.py new file mode 100644 index 0000000..c567002 --- /dev/null +++ b/PUQ/designmethods/sequential_1d_stochastic.py @@ -0,0 +1,191 @@ +import numpy as np +import matplotlib.pyplot as plt +from PUQ.designmethods.support import multiple_pdfs, multiple_determinants +from PUQ.designmethods.gen_funcs.acquisition_1d_stochastic import ( + var, + ivar, + imse, + lookahead, +) +from PUQ.surrogate import emulator + + +class sequential_design: + def __init__(self, cls_func, trace=False): + self.cls_func = cls_func + self.trace = trace + self.y = self.cls_func.real_data + self.d = self.cls_func.d + self.Sigma = self.cls_func.obsvar + self.Sigma3d = self.Sigma.reshape(1, self.d, self.d) + self.detSigma = np.linalg.det(self.Sigma) + return + + def __getitem__(self, key): + return self.__dict__[key] + + def __setitem__(self, item, value): + self.__dict__[item] = value + + def get(self, key): + return self.__dict__.get(key) + + def build_design(self, z0, f0, T, persis_info, af, test=None, args={}): + + if test is not None: + self.test_data_gen(test) + + H = [] + timel = [] + metric_sum = {} + for t in range(0, T): + print(f"t: {t}") if self.trace else None + + model = emulator( + x=z0, + theta=np.array([0]), + f=f0, + method="hetGP", + args={"known": {"beta0": np.mean(f0)}}, + ) + model.fit() + + args["seed"] += 1 + + if test is not None: + metric_sum = self.eval_perf(model, args.get("extra_metric", True)) + + acq_func = eval(af)(model=model, cls_func=self.cls_func, args=args) + + acq_func.acquire_new() + znew = acq_func.znew + fnew = self.cls_func.sim_f(znew.flatten(), persis_info=persis_info).reshape( + 1, 1 + ) + + f0 = np.concatenate((f0, fnew), axis=0) + z0 = np.concatenate((z0, znew), axis=0) + + H.append( + { + "t": t, + "new": acq_func.new, + "h": getattr(acq_func, "h", None), + "f": fnew, + "z": znew, + "MSE": metric_sum.get("MSE", None), + "MAD": metric_sum.get("MAD", None), + "VAR": metric_sum.get("VAR", None), + "MSEy": metric_sum.get("MSEy", None), + "MADy": metric_sum.get("MADy", None), + "MSEn": metric_sum.get("MSEn", None), + "MADn": metric_sum.get("MADn", None), + "summary_metric": metric_sum.get("sum_metric", None), + } + ) + + unique_rows, counts = np.unique(z0, axis=0, return_counts=True) + + self.xt = unique_rows + self.reps = counts + self.fs = f0 + self.zs = z0 + self.time = timel + self.H = H + + return self + + def test_data_gen(self, test): + + # Gen test data + self.ttest, self.ptest, self.wtest, self.ntest, self.ftest = ( + test["theta"], + test["p"], + test["w"], + test["noise"], + test["f"], + ) + # plt.scatter(self.ttest[:, 0], self.ttest[:, 1], c=self.ftest) + # plt.show() + + # plt.scatter(self.ttest[:, 0], self.ttest[:, 1], c=self.ntest) + # plt.show() + + x_tiled = np.tile(self.cls_func.x, (self.ttest.shape[0], 1)) + t_repeated = np.repeat(self.ttest, self.cls_func.x.shape[0], axis=0) + self.ztest = np.hstack([x_tiled, t_repeated]) + + # To be used for performance evaluations + ntot, nm, d = self.ztest.shape[0], self.ttest.shape[0], self.cls_func.d + + # to construct S matrix + self.idr = np.arange(0, ntot)[:, None] + idc = np.arange(0, ntot).reshape(nm, d) + self.idc = np.repeat(idc, repeats=d, axis=0) + + def eval_perf(self, model, extra_metric=False): + + nm, d = self.ttest.shape[0], self.cls_func.d + # sc = plt.scatter(self.ztest[:, 1], self.ztest[:, 2], c=self.wtest, cmap="viridis") # Use any colormap + # plt.colorbar(sc, label="wtest values") # Add a colorbar + # plt.show() + + # predict at mesh + pr = model.predict(x=self.ztest, thetaprime=self.ztest) + + # ntot, ntot x ntot, ntot + # mu, Sn, nugs = pr["mean"], pr["cov"], pr["nugs"] + mu, Sn, nugs = pr._info["mean"], pr._info["covmat"], pr._info["nugs"] + + muT = mu.reshape(nm, d) + S = Sn[self.idr, self.idc].reshape(nm, d, d) + + N = S + self.Sigma3d + g = multiple_pdfs(self.y, muT, N) + + MSE = np.mean(((g.flatten() - self.ptest.flatten()) ** 2) * self.wtest) + MAD = np.mean(np.abs(g.flatten() - self.ptest.flatten()) * self.wtest) + + M = S + 0.5 * self.Sigma3d + f = multiple_pdfs(self.y, muT, M) + + twopiddet = (2**self.d) * (np.sqrt(np.pi) ** self.d) * np.sqrt(self.detSigma) + VAR = np.mean(((1 / twopiddet) * f - g**2) * self.wtest) + + if extra_metric: + diff_y = (mu.flatten() - self.ftest.flatten()).reshape(nm, d) + diff_n = (nugs.flatten() - self.ntest.flatten()).reshape(nm, d) + + MSEy = np.mean(np.mean(diff_y**2, axis=1).flatten() * self.wtest) + MADy = np.mean(np.mean(np.abs(diff_y), axis=1).flatten() * self.wtest) + + MSEn = np.mean(np.mean(diff_n**2, axis=1).flatten() * self.wtest) + MADn = np.mean(np.mean(np.abs(diff_n), axis=1).flatten() * self.wtest) + + # Weighted absolute differences + f_w = np.mean(np.abs(diff_y) * self.wtest[:, None], axis=0) + n_w = np.mean(np.abs(diff_n) * self.wtest[:, None], axis=0) + + # Unweighted absolute differences + f_wh = np.mean(np.abs(diff_y), axis=0) + n_wh = np.mean(np.abs(diff_n), axis=0) + summary = {} + for di in range(d): + summary[f"f{di+1}w"] = f_w[di] + summary[f"f{di+1}"] = f_wh[di] + summary[f"n{di+1}w"] = n_w[di] + summary[f"n{di+1}"] = n_wh[di] + else: + MSEy, MADy, MSEn, MADn, summary = 0, 0, 0, 0, 0 + + metric_sum = { + "MSE": MSE, + "MAD": MAD, + "VAR": VAR, + "MSEy": MSEy, + "MADy": MADy, + "MSEn": MSEn, + "MADn": MADn, + "summary": summary, + } + return metric_sum diff --git a/PUQ/designmethods/sequential_md_deterministic.py b/PUQ/designmethods/sequential_md_deterministic.py new file mode 100644 index 0000000..b1b704d --- /dev/null +++ b/PUQ/designmethods/sequential_md_deterministic.py @@ -0,0 +1,128 @@ +import numpy as np +from PUQ.designmethods.support import multiple_pdfs, multiple_determinants +from PUQ.designmethods.gen_funcs.acquisition_md_deterministic import ( + ivar, + var, + imse, + rnd, + exp, +) +import time +from PUQ.surrogate import emulator + + +class sequential_design: + def __init__(self, cls_func, trace=False): + self.cls_func = cls_func + self.trace = trace + self.y = self.cls_func.real_data + self.d = self.cls_func.d + self.Sigma = self.cls_func.obsvar + self.Sigma3d = self.Sigma.reshape(1, self.d, self.d) + self.detSigma = np.linalg.det(self.Sigma) + return + + def __getitem__(self, key): + return self.__dict__[key] + + def __setitem__(self, item, value): + self.__dict__[item] = value + + def get(self, key): + return self.__dict__.get(key) + + def build_design(self, z0, f0, T, af, test=None, args={}): + + self.test = test + args["rand_stream"] = np.random.default_rng(args["seed"]) + H = [] + timel = [] + metric_sum = {} + + md = np.arange(f0.shape[1]).reshape(f0.shape[1], 1) + for t in range(0, T): + print(f"t: {t}") if self.trace else None + + tic = time.time() + + model = emulator( + x=z0, + theta=md, + f=f0, + method="multihomGP", + args={ + "lower": None, + "upper": None, + "noiseControl": { + "k_theta_g_bounds": (1, 100), + "g_max": 1e2, + "g_bounds": (1e-6, 1), + }, + "init": {}, + "known": {}, + "settings": { + "linkThetas": "joint", + "logN": True, + "initStrategy": "residuals", + "checkHom": True, + "penalty": True, + "trace": 0, + "return.matrices": True, + "return.hom": False, + "factr": 1e9, + }, + }, + ) + + toc = time.time() + timel.append(toc - tic) + + args["seed"] += 1 + + if test is not None: + metric_sum = self.eval_perf(model) + + acq_func = eval(af)(model=model, cls_func=self.cls_func, args=args) + + acq_func.acquire_new() + tnew = acq_func.znew + + fnew = self.cls_func.function(*tnew.flatten()).reshape(1, self.cls_func.d) + + f0 = np.concatenate((f0, fnew), axis=0) + z0 = np.concatenate((z0, tnew), axis=0) + + H.append({"t": t, "f": fnew, "z": tnew, "MAD": metric_sum.get("MAD", None)}) + + unique_rows, counts = np.unique(z0, axis=0, return_counts=True) + + self.t = unique_rows + self.reps = counts + self.fs = f0 + self.zs = z0 + self.time = timel + self.H = H + + return self + + def eval_perf(self, model): + + nm, d = self.test["theta"].shape[0], self.cls_func.d + + # predict at mesh + pr = model.predict(x=self.test["theta"], thetaprime=self.test["theta"]) + + # ntot, ntot x ntot, ntot + mu, Sn = pr._info["mean"], pr._info["S"] + + muT = mu.reshape(nm, d) + S = Sn.transpose(2, 0, 1) # Sn[self.idr, self.idc].reshape(nm, d, d) + + N = S + self.Sigma3d + g = multiple_pdfs(self.y, muT, N) + + MSE = np.mean(((g.flatten() - self.test["p"].flatten()) ** 2)) + MAD = np.mean(np.abs(g.flatten() - self.test["p"].flatten())) + + metric_sum = {"MAD": MAD, "MSE": MSE} + return metric_sum diff --git a/PUQ/designmethods/sequential_md_stochastic.py b/PUQ/designmethods/sequential_md_stochastic.py new file mode 100644 index 0000000..7f0fe6c --- /dev/null +++ b/PUQ/designmethods/sequential_md_stochastic.py @@ -0,0 +1,219 @@ +import numpy as np +from PUQ.designmethods.gen_funcs.allocate_reps_md import allocate +from PUQ.designmethods.gen_funcs.acquisition_md_stochastic import acquire +from PUQ.designmethods.gen_funcs.batch_acquisition_funcs_support import ( + multiple_pdfs, + build_emulator, +) +from joblib import Parallel, delayed +import copy + + +class sequential_design: + def __init__(self, cls_func, trace=True): + self.cls_func = cls_func + self.trace = trace + self.y = self.cls_func.real_data + self.x = self.cls_func.x + self.d = self.cls_func.d + self.Sigma = self.cls_func.obsvar + self.Sigma3d = self.Sigma.reshape(1, self.d, self.d) + self.detSigma = np.linalg.det(self.Sigma) + return + + def __getitem__(self, key): + return self.__dict__[key] + + def __setitem__(self, item, value): + self.__dict__[item] = value + + def get(self, key): + return self.__dict__.get(key) + + def build_design(self, t0, f0, af, args={}): + # data params + + b = args["batch_size"] + max_iter = args["max_iter"] + alloc_settings = args["alloc_settings"] + des_settings = args.get("des_settings", None) + pc_settings = args.get("pc_settings", None) + test_data = args["data_test"] + prior_func = args["prior"] + # explore params + rho = alloc_settings.get("rho") + if rho is not None: + b_new = int(b * rho) + r_new = int(b / b_new) + + seed = 1 + + persis_info = {"rand_stream": np.random.default_rng(1)} + + persis_info = {i: {} for i in range(b)} # add_unique_random_streams({}, b) + for perid, per in enumerate(persis_info): + persis_info[perid]["rand_stream"] = np.random.default_rng(seed * b + perid) + + rand_stream = np.random.default_rng( + seed * (b + 1) + ) # np.random.Generator(np.random.PCG64()) + + f_c, t_c = f0, t0 + + iter_explore, iter_exploit = 0, 0 + is_explore, is_exploit = des_settings.get("is_explore"), des_settings.get( + "is_exploit" + ) + + for iteration in range(max_iter): + + emu, TV = self.newiteration( + t_c, f_c, self.x, pc_settings, test_data, self.Sigma, self.y + ) + + theta, iter_exploit, iter_explore = self.onebatch( + b, + b_new, + r_new, + emu, + prior_func, + af, + self.cls_func, + test_data, + alloc_settings, + pc_settings, + des_settings, + iter_exploit, + iter_explore, + is_exploit, + is_explore, + rand_stream, + self.trace, + ) + + fevals = Parallel(n_jobs=b)( + delayed(self.cls_func.sim_f)(theta[i], persis_info[i]) for i in range(b) + ) + + fevals = np.array(fevals).T + + f_c = np.concatenate((f_c, fevals), axis=1) + t_c = np.concatenate((t_c, theta), axis=0) + + unique_rows, counts = np.unique(t_c, axis=0, return_counts=True) + self.f = f_c + self.theta = t_c + self.theta0 = unique_rows + self.rep0 = counts + + def newiteration(self, theta, fevals, x, pc_settings, test_data, obsvar, obs): + + thetatest, ptest, ftest, priortest = None, None, None, None + if test_data is not None: + thetatest, ptest, ftest, priortest = ( + test_data["theta"], + test_data["p"], + test_data["f"], + test_data["p_prior"], + ) + + emu = build_emulator(x, theta, fevals, pc_settings) + + d = len(x) + obsvar3d = obsvar.reshape(1, d, d) + + # ntest x d + # pred = emu.predict(x=x, theta=thetatest) + pred = emu.predict(x=thetatest) + + mu = pred.mean().T + S = pred._info["S"] + St = np.transpose(S, (2, 0, 1)) + N = St + obsvar3d + phat = multiple_pdfs(obs, mu, N) + + # Obtain the accuracy on the test set + if ptest is not None: + TV = np.mean(np.abs(ptest - phat)) + + return emu, TV + + def onebatch( + self, + b, + b_new, + r_new, + emu, + prior_func, + acqfunc, + data_cls, + test_data, + alloc_settings, + pc_settings, + des_settings, + iter_exploit, + iter_explore, + is_exploit, + is_explore, + rand_stream, + trace, + ): + + ivar_exploit, ivar_explore = np.inf, np.inf + + emu_original_info = copy.deepcopy(emu._info) + + if is_exploit: + # Allocate existing ones + allocate_obj = allocate( + budget=b, + emu_info=emu, + prior=prior_func, + func_cls=data_cls, + theta_mesh=test_data["theta"], + method=alloc_settings.get("method"), + alloc_settings=alloc_settings, + rand_stream=rand_stream, + trace=trace, + ) + allocate_obj.allocatereps() + if not is_explore: + ivar_exploit = 0 + else: + ivar_exploit = allocate_obj.ivar_exploit(emu, pc_settings) + + r_exploit = allocate_obj.reps + theta_exploit = allocate_obj.theta + + emu._info = emu_original_info + + if is_explore: + # Find new ones + acquire_obj = acquire( + bnew=b_new, + rep=r_new, + emu=emu, + func_cls=data_cls, + theta_mesh=test_data["theta"], + prior=prior_func, + method=acqfunc, + nL=des_settings.get("nL"), + pc_settings=pc_settings, + rand_stream=rand_stream, + impute_str=des_settings.get("impute_str"), + skip=not is_exploit, + ) + + acquire_obj.acquire_new() + + ivar_explore = acquire_obj.ivar + theta_explore = acquire_obj.tnew + + if ivar_exploit <= ivar_explore: + new_theta = np.repeat(theta_exploit, r_exploit, axis=0) + iter_exploit += 1 + else: + new_theta = np.repeat(theta_explore, r_new, axis=0) + iter_explore += 1 + + return new_theta, iter_exploit, iter_explore diff --git a/PUQ/designmethods/support.py b/PUQ/designmethods/support.py new file mode 100644 index 0000000..6bd6964 --- /dev/null +++ b/PUQ/designmethods/support.py @@ -0,0 +1,38 @@ +import numpy as np +from numpy.linalg import inv, det + + +def multiple_pdfs(x, means, covs): + # Cite: http://gregorygundersen.com/blog/2020/12/12/group-multivariate-normal-pdf/ + + # NumPy broadcasts `eigh`. + vals, vecs = np.linalg.eigh(covs) + + # Compute the log determinants across the second axis. + logdets = np.sum(np.log(vals), axis=1) + + # Invert the eigenvalues. + valsinvs = 1.0 / vals + + # Add a dimension to `valsinvs` so that NumPy broadcasts appropriately. + Us = vecs * np.sqrt(valsinvs)[:, None] + devs = x - means + + # Use `einsum` for matrix-vector multiplications across the first dimension. + devUs = np.einsum("ni,nij->nj", devs, Us) + + # Compute the Mahalanobis distance by squaring each term and summing. + mahas = np.sum(np.square(devUs), axis=1) + + # Compute and broadcast scalar normalizers. + dim = len(vals[0]) + log2pi = np.log(2 * np.pi) + return np.exp(-0.5 * (dim * log2pi + mahas + logdets)) + + +def multiple_determinants(covs): + vals, vecs = np.linalg.eigh(covs) + # Compute the log determinants across the second axis. + # dets = np.prod(vals, axis=1) + logdets = np.sum(np.log(vals), axis=1) + return np.exp(logdets) # dets#np.exp(logdets) diff --git a/PUQ/designmethods/utils.py b/PUQ/designmethods/utils.py deleted file mode 100644 index caee712..0000000 --- a/PUQ/designmethods/utils.py +++ /dev/null @@ -1,107 +0,0 @@ -import argparse -import dill as pickle -import os - - -def parse_arguments(): - parser = argparse.ArgumentParser("Parameters for calibration") - parser.add_argument( - "-nworkers", metavar="N2", default=2, type=int, help="Number of workers." - ) - parser.add_argument( - "-minibatch", metavar="N2", default=1, type=int, help="Minibatch size." - ) - parser.add_argument( - "-max_eval", - metavar="N2", - default=100, - type=int, - help="Number of parameters to acquire.", - ) - parser.add_argument( - "-n_init_thetas", - metavar="N2", - default=8, - type=int, - help="Number of parameters from LHS.", - ) - parser.add_argument("-seed_n0", metavar="N2", default=1, type=int, help="Seed No.") - parser.add_argument( - "-init_seeds", metavar="N2", default=1, type=int, help="Init Seed No." - ) - parser.add_argument( - "-final_seeds", metavar="N2", default=2, type=int, help="Final Seed No." - ) - parser.add_argument( - "-al_func", - metavar="N2", - default="eivar", - type=str, - help="Acquisition function.", - ) - parser.add_argument( - "-funcname", - metavar="N2", - default="unimodal", - type=str, - help="Name of the function.", - ) - parser.add_argument( - "-candsize", metavar="N2", default=100, type=int, help="Candidate size." - ) - parser.add_argument( - "-refsize", metavar="N2", default=100, type=int, help="Reference list size." - ) - parser.add_argument( - "-believer", metavar="N2", default=0, type=int, help="Kriging believer type." - ) - args = parser.parse_args() - return args - - -def save_output(desing_obj, name, al_func, nworker, minibatch, seedno): - outputname = ( - "output_" + name + "_" + al_func + "_w_" + str(nworker) + "_b_" + str(minibatch) - ) - if not os.path.isdir(outputname): - os.mkdir(outputname) - - design_path = ( - outputname - + "/" - + name - + "_" - + al_func - + "_w_" - + str(nworker) - + "_b_" - + str(minibatch) - + "_seed_" - + str(seedno) - + ".pkl" - ) - with open(design_path, "wb") as file: - pickle.dump(desing_obj, file) - - -def read_output(path1, name, al_func, nworker, minibatch, seedno): - outputname = "output" # + name + '_' + al_func + '_w_' + str(nworker) + '_b_' + str(minibatch) - design_path = ( - path1 - + outputname - + "/" - + name - + "_" - + al_func - + "_w_" - + str(nworker) - + "_b_" - + str(minibatch) - + "_seed_" - + str(seedno) - + ".pkl" - ) - with open(design_path, "rb") as file: - design_obj = pickle.load(file) - - return design_obj diff --git a/PUQ/prior.py b/PUQ/prior.py index 73add02..657e64e 100644 --- a/PUQ/prior.py +++ b/PUQ/prior.py @@ -9,90 +9,108 @@ def __init__(self, a, b): self.p = len(a) def rnd(self, n, seed=None): - if seed == None: - pass - else: - np.random.seed(seed) + # if seed == None: + # pass + # else: + # np.random.seed(seed) thlist = [] for i in range(self.p): - thlist.append(sps.uniform.rvs(self.a[i], self.b[i] - self.a[i], size=n)) + thlist.append(seed.uniform(self.a[i], self.b[i], size=n)) + # thlist.append(sps.uniform.rvs(self.a[i], self.b[i] - self.a[i], size=n)) return np.array(thlist).T - def pdf(self, theta): - ncnd = theta.shape[0] - thlist = np.ones(ncnd) - for i in range(self.p): - thlist *= sps.uniform.pdf(theta[:, i], self.a[i], self.b[i] - self.a[i]) - return np.array(thlist).T - - -class prior_truncnorm: - def __init__(self, a, b, loc, scale): - self.a = a - self.b = b - self.loc = loc - self.scale = scale - self.p = len(a) - - def rnd(self, n, seed=None): - if seed == None: - pass - else: - np.random.seed(seed) - thlist = [] - for i in range(self.p): - thlist.append( - sps.truncnorm.rvs( - a=self.a[i], - b=self.b[i], - loc=self.loc[i], - scale=self.scale[i], - size=n, - ) - ) - return np.array(thlist).T - def pdf(self, theta): - ncnd = theta.shape[0] - thlist = np.ones(ncnd) - for i in range(self.p): - thlist *= sps.truncnorm.pdf( - theta[:, i], - a=self.a[i], - b=self.b[i], - loc=self.loc[i], - scale=self.scale[i], - ) - return np.array(thlist).T - - # if seed == None: - # pass - # else: - # np.random.seed(seed) - # sps.truncnorm.rvs(a=-2.5, b=1.5, loc=3, scale=2, size=1000) - # 1.5=(6-3)/2, -2.5=(-2-3)/2 - # 3=(6-3)/1, -5=(-2-3)/1 - # 6=(6-3)/0.5, -10=(-2-3)/0.5 - # class prior_norm: - # def rnd(n): - # thlist = [] - # for i in range(thetalimits.shape[0]): - # thlist.append(sps.truncnorm.rvs(a=-10, b=2, loc=3, scale=1, size=n)) - # return np.array(thlist).T - - # def pdf(thetacnd): - # ncnd = thetacnd.shape[0] - # thlist = np.ones(ncnd) - # for i in range(thetalimits.shape[0]): - # thlist *= sps.truncnorm.pdf(thetacnd[:, i], a=-10, b=2, loc=3, scale=1) - # return np.array(thlist).T - - # if rnd == True: - # thetas = prior_norm.rnd(n) - # return thetas - # else: - # thetapdf = prior_norm.pdf(thetacnd) - # return thetapdf +# class prior_uniform: +# def __init__(self, a, b): +# self.a = a +# self.b = b +# self.p = len(a) + +# def rnd(self, n, seed=None): +# if seed == None: +# pass +# else: +# np.random.seed(seed) +# thlist = [] +# for i in range(self.p): +# thlist.append(sps.uniform.rvs(self.a[i], self.b[i] - self.a[i], size=n)) +# return np.array(thlist).T + +# def pdf(self, theta): +# ncnd = theta.shape[0] +# thlist = np.ones(ncnd) +# for i in range(self.p): +# thlist *= sps.uniform.pdf(theta[:, i], self.a[i], self.b[i] - self.a[i]) +# return np.array(thlist).T + + +# class prior_truncnorm: +# def __init__(self, a, b, loc, scale): +# self.a = a +# self.b = b +# self.loc = loc +# self.scale = scale +# self.p = len(a) + +# def rnd(self, n, seed=None): +# if seed == None: +# pass +# else: +# np.random.seed(seed) +# thlist = [] +# for i in range(self.p): +# thlist.append( +# sps.truncnorm.rvs( +# a=self.a[i], +# b=self.b[i], +# loc=self.loc[i], +# scale=self.scale[i], +# size=n, +# ) +# ) +# return np.array(thlist).T + +# def pdf(self, theta): +# ncnd = theta.shape[0] +# thlist = np.ones(ncnd) +# for i in range(self.p): +# thlist *= sps.truncnorm.pdf( +# theta[:, i], +# a=self.a[i], +# b=self.b[i], +# loc=self.loc[i], +# scale=self.scale[i], +# ) +# return np.array(thlist).T + +# if seed == None: +# pass +# else: +# np.random.seed(seed) +# sps.truncnorm.rvs(a=-2.5, b=1.5, loc=3, scale=2, size=1000) +# 1.5=(6-3)/2, -2.5=(-2-3)/2 +# 3=(6-3)/1, -5=(-2-3)/1 +# 6=(6-3)/0.5, -10=(-2-3)/0.5 +# class prior_norm: +# def rnd(n): +# thlist = [] +# for i in range(thetalimits.shape[0]): +# thlist.append(sps.truncnorm.rvs(a=-10, b=2, loc=3, scale=1, size=n)) +# return np.array(thlist).T + +# def pdf(thetacnd): +# ncnd = thetacnd.shape[0] +# thlist = np.ones(ncnd) +# for i in range(thetalimits.shape[0]): +# thlist *= sps.truncnorm.pdf(thetacnd[:, i], a=-10, b=2, loc=3, scale=1) +# return np.array(thlist).T + +# if rnd == True: +# thetas = prior_norm.rnd(n) +# return thetas +# else: +# thetapdf = prior_norm.pdf(thetacnd) +# return thetapdf def prior_dist(dist="uniform"): diff --git a/PUQ/surrogate.py b/PUQ/surrogate.py index 8fa40e8..e131010 100644 --- a/PUQ/surrogate.py +++ b/PUQ/surrogate.py @@ -4,8 +4,8 @@ import numpy as np import importlib -import copy import warnings +import copy, types class emulator(object): @@ -14,6 +14,7 @@ def __init__( x=None, theta=None, f=None, + thetaprime=None, method="PCGP", passthroughfunc=None, args={}, @@ -85,6 +86,7 @@ def __init__( self.__ptf = passthroughfunc if self.__ptf is not None: return + self._args = copy.deepcopy(args) if f is not None: @@ -205,8 +207,8 @@ def __repr__(self): ) return strrepr - def __call__(self, x=None, theta=None, args=None): - return self.predict(x, theta, args) + def __call__(self, x=None, theta=None, thetaprime=None, args=None): + return self.predict(x, theta, thetaprime, args) def fit(self, args=None): """ @@ -221,15 +223,15 @@ def fit(self, args=None): Optional dictionary containing options you would like to pass to fit function. It will add/modify those in self._args. """ - if args is not None: argstemp = {**self._args, **copy.deepcopy(args)} else: argstemp = copy.copy(self._args) x, theta, f = self.__preprocess() + self.method.fit(self._info, x, theta, f, **argstemp) - def predict(self, x=None, theta=None, args={}): + def predict(self, x=None, theta=None, thetaprime=None, args={}): """ Fits an emulator or surrogate. @@ -336,443 +338,36 @@ def predict(self, x=None, theta=None, args={}): ) info = {} - self.method.predict(info, self._info, x, theta, **argstemp) + self.method.predict(info, self._info, x, theta, thetaprime, **argstemp) return prediction(info, self) - def acquisition(self, x=None, theta1=None, theta2=None): - return self.method.acquisition(self._info, x, theta1, theta2) - - def supplement( - self, - size, - x=None, - xchoices=None, - theta=None, - thetachoices=None, - choicescost=None, - cal=None, - args=None, - overwrite=False, - removereps=None, - ): - """ - Chooses new theta or x to be investigated. - - .. important:: - A user must provide either x or theta (or cal). - - :Example: - .. code-block:: python - - emulator.supplement(size=size, theta=theta) - - Parameters - ---------- - size : int - The number of new supplements to return. - - .. note:: - - If only theta is supplied, returns at most size of those. - - If only x is supplied, returns at most size of those. - - If both x and theta are supplied, then size will be less - than the product of the number of returned theta and the - number of x. - - x : numpy.ndarray, optional - An array of parameters where to predict. - The default is None. - xchoices : numpy.ndarray, optional - An array of inputs to select from. - If not provided, a subset of x is used. The default is None. - - .. warning:: self.__suppx has not developed yet. - - theta : numpy.ndarray, optional - An array of parameters where to predict. The default is None. - thetachoices : numpy.ndarray, optional - An array of parameters to select from. - If not provided, a subset of x is used. The default is None. - choicescost : numpy.ndarray, optional - An array of positive cost of each element in choice. - The default is None. - cal : surmise.calibration.calibrator, optional - A calibrator object that contains information about calibration. - The default is None. - args : dict, optional - A dictionary containing options to pass to the method. - The default is None. If not provided, defaults to the one used to - build the emulator. - overwrite : boolean, optional - True if an existing supplement is replaced. If False, and one - exists, returns without doing anything. The default is False. - removereps : boolean, optional - True if any replications existing supplement is removed. - The default is None. If not provided, defaults to the one used to - build the emulator. - - Raises - ------ - ValueError - If the dimensions do not match the fitted emulator - - Returns - ------- - numpy.ndarray - self.__thetasupp (or self.__x) - numpy.ndarray - suppinfo - - """ - - if args is not None: - argstemp = {**self._args, **args} - else: - argstemp = self._args - - if removereps is None: - if x is not None: - removereps = not self.__options["xreps"] - if theta is not None: - removereps = not self.__options["thetareps"] - - if size < 1: - if size == 0: - if self.__supptheta is None: - raise ValueError("No self.__supptheta exists.") - else: - print("Returning self.__supptheta.") - return copy.deepcopy(self.__supptheta) - else: - raise ValueError("Size should be a positive integer.") - - if cal is not None: - try: - if theta is None: - theta = cal.theta(1000) - if self.__theta.shape[1] != theta.shape[1]: - raise ValueError("cal.theta(n) produces the wrong " "shape.") - except Exception: - raise ValueError("cal.theta(2000) failed.") - - if (theta is None) and (x is None): - raise ValueError("Provide either x or (theta or cal).") - else: - if x is not None: - if theta is not None: - raise ValueError("Provide either x or (theta or cal).") - else: - raise ValueError("Supplement x has not supported yet.") - elif theta is not None: - if self.__theta.shape[1] == theta.shape[1]: - if thetachoices is None: - if theta.shape[0] > 30 * size: - thetachoices = theta[ - np.random.choice( - theta.shape[0], 30 * size, replace=False - ), - :, - ] - else: - thetachoices = copy.copy(theta) - else: - if thetachoices.shape[1] != theta.shape[1]: - raise ValueError( - "Dimensions of choices and " - "predictions are not aligning." - ) - if choicescost is None: - choicescost = np.ones(thetachoices.shape[0]) - else: - if thetachoices.shape[0] != choicescost.shape[0]: - raise ValueError("choicecost is not the right " "shape.") - else: - raise ValueError( - "theta has the wrong shape, it does not " - "match emu._emulator__theta." - ) - - try: - supptheta, suppinfo = self.method.supplementtheta( - self._info, - copy.copy(size), - copy.copy(theta), - copy.copy(thetachoices), - copy.copy(choicescost), - copy.copy(cal), - **argstemp - ) - except Exception: - raise ValueError("supplementtheta does not exist.") - - if supptheta is not None: - if removereps: - nctheta, ctheta, rtheta = _matrixmatching(self.__theta, supptheta) - if nctheta.shape[0] < 0.5: - supptheta = None - raise ValueError( - "supptheta is a complete replication of " "self.__theta." - ) - else: - if nctheta.shape[0] < supptheta.shape[0]: - print("Removing replications from supptheta.") - supptheta = supptheta[nctheta, :] - - if (self.__supptheta is not None) and (not overwrite): - raise ValueError( - "Either evaluate self.__supptheta or " "select overwrite = True." - ) - else: - self.__supptheta = copy.copy(supptheta) - return copy.copy(self.__supptheta), suppinfo - else: - raise ValueError("method.supplementtheta provides None.") - - def update(self, x=None, theta=None, f=None, args=None, options=None): - """ - Updates and refits the emulator. - - :Example: - .. code-block:: python - - emulator.update(x=x) # Replace self.__x with x - - emulator.update(theta=theta) # Replace self.__theta with theta - - emulator.update(f=f) # Replace self.__f with f if - # self.__supptheta is None. Otherwise, - # update self.__theta - # with (self.__theta, self.__supptheta) - # and self.__f with (self.__f, f) - - emulator.update(x=x, f=f) # Update self.__x with (self.__x, x) - # and self.__f with (self.__f, f) + def update(self, x=None, Y=None, **kwargs): - emulator.update(theta=theta, f=f) # Update self.__theta with - # (self.__theta, theta) and - # self.__f with (self.__f, f) - - .. warning:: self.__suppx has not developed yet. - - Parameters - ---------- - x : numpy.ndarray, optional - xs you would like to append. The default is None. - - theta : numpy.ndarray, optional - thetas you would like to append. Defaults to emu.__supptheta. - The default is None. - - f : numpy.ndarray, optional - An array of responses. The default is None. - - args : dict, optional - A dictionary containing options you would like to pass to - [method].update(f, theta, x, args). - Defaults to the one used to build the emulator. - - options : dict, optional - A dictionary containing options to build the emulator. - Modify with update when you want to change it. - The default is None. + self.method.update(self._info, x=x, Y=Y, **kwargs) - Raises - ------ - ValueError - If the dimensions of inputs do not match with the existing - emulator. + def acquisition(self, x=None, theta1=None, theta2=None): + return self.method.acquisition(self._info, x, theta1, theta2) - Returns - ------- - None. + def computeC(self, x=None, theta1=None, realdata=None, realvar=None): - """ + return self.method.computeC(self._info, x, theta1, realdata, realvar) - if options is not None: - self.__optionsset(copy.copy(options)) - if args is not None: - self._args = {**self._args, **copy.deepcopy(args)} - - if (theta is not None) and (x is not None): - # provide either x or theta for now - raise ValueError( - "Adding new theta and x at once is currently not " - "supported. Supply either theta or x." - ) - elif f is not None: - if (theta is None) and (x is None): - if f.shape[0] == self.__f.shape[0]: - # no of rows of f (and x) is still the same - if self.__supptheta is not None: - if f.shape[1] == self.__supptheta.shape[0]: - if self.__options["thetareps"]: - # update with self.__supptheta and f - self.__theta = np.vstack( - (self.__theta, self.__supptheta) - ) - self.__f = np.hstack((self.__f, f)) - self.__supptheta = None - else: - # identify matches - nc, c, r = _matrixmatching( - self.__theta, self.__supptheta - ) - # update __f with f for the matches - self.__f[:, r] = f[:, c] - if nc.shape[0] > 0.5: - f = f[:, nc] - supptheta = self.__supptheta[nc, :] - self.__f = np.hstack((self.__f, f)) - self.__theta = np.vstack((self.__theta, supptheta)) - self.__supptheta = None - else: - raise ValueError( - "Could not resolve absense of " - " theta, please provide theta." - ) - elif f.shape[1] == self.__theta.shape[0]: - # just updating f - self.__f = f - else: - raise ValueError( - "Could not resolve absense of theta, " - "please provide theta" - ) - else: - # no of rows of f (and x) is not the same - raise ValueError("Could not resolve absense of x. Provide " "x.") - elif theta is not None: - if ( - (f.shape[0] == self.__f.shape[0]) - and (f.shape[1] == theta.shape[0]) - and (theta.shape[1] == self.__theta.shape[1]) - ): - if self.__options["thetareps"]: - # if replicated thetas are allowed - self.__theta = np.vstack((self.__theta, theta)) - self.__f = np.hstack((self.__f, f)) - else: - # if replicated thetas are not allowed - nc, c, r = _matrixmatching(self.__theta, theta) - self.__f[:, r] = f[:, c] - if nc.shape[0] > 0.5: - # append the unmatched thetas - f = f[:, nc] - theta = theta[nc, :] - self.__f = np.hstack((self.__f, f)) - self.__theta = np.vstack((self.__theta, theta)) - else: - raise ValueError( - "Check the dimensions of theta and f. " - "Possible solutions: " - "1) Use emu.update(theta=theta) to " - "update the emulator first. " - "2) Provide x for alignment." - ) + def __deepcopy__(self, memo): + # create a blank instance without calling __init__ + new = self.__class__.__new__(self.__class__) + memo[id(self)] = new - elif x is not None: - if ( - (f.shape[1] == self.__f.shape[1]) - and (f.shape[0] == x.shape[0]) - and (x.shape[1] == self.__x.shape[1]) - ): - if options["xreps"]: - # if replicated xs are allowed - self.__x = np.vstack((self.__x, x)) - self.__f = np.vstack((self.__f, f)) - else: - # if replicated xs are not allowed - nc, c, r = _matrixmatching(self.__x, x) - self.__f[r, :] = f[c, :] - if nc.shape[0] > 0.5: - # append the unmatched xs - f = f[nc, :] - x = x[nc, :] - self.__f = np.vstack((self.__f, f)) - self.__x = np.vstack((self.__x, x)) - else: - raise ValueError( - "Check the dimensions of x and f. " - "Possible solutions: " - "1) Use emu.update(x=x) to " - "update the emulator first. " - "2) Provide theta for alignment." - ) - elif x is not None: # theta None, f None - # Update self.__x with x - if x.shape[0] != self.__f.shape[0]: - raise ValueError( - "Number of rows of x is changed but new f is " "not provided." - ) + for k, v in self.__dict__.items(): + if isinstance(v, types.ModuleType): + # leave module references as-is + setattr(new, k, v) + elif k == "_info": + # force deep copy of _info so updates won't affect the original + setattr(new, k, copy.deepcopy(v, memo)) else: - self.__x = x - elif theta is not None: # x None, f None - # Update self.__theta with theta - if theta.shape[0] != self.__f.shape[1]: - raise ValueError( - "Number of rows of theta is changed but new " "f is not provided." - ) - else: - self.__theta = theta - - if "update" in dir(self.method): - x, theta, f = self.__preprocess() - self.method.update(self._info, x, theta, f, **self._args) - else: - if self.__options["autofit"]: - self.fit() - - return - - def remove(self, x=None, theta=None, cal=None, options=None): - """ - Removes either x or theta, and the corresponding f values from - the fitted emulator, and refits the emulator. - - :Example: - - .. code-block:: python - - emlator.remove(theta=theta) - - Parameters - ---------- - x : numpy.ndarray, optional - x to remove from self.__x. The default is None. - theta : numpy.ndarray, optional - theta to remove from self.__theta. The default is None. - cal : surmise.calibration.calibrator, optional - A calibrator class instance as defined in surmise.calibration. - The default is None. - options : dict, optional - A dictionary containing options to build the emulator. - The default is None. - - Returns - ------- - None. - - """ - - if cal is not None: - totalseen = np.where( - np.mean(np.logical_not(np.isfinite(self.__f)), 0) - < self.__options["thetarmnan"] - )[0] - lpdf_ex = cal.theta.lpdf(self.__theta[totalseen, :]) - thetasort = np.argsort(lpdf_ex) - m_cutoff = max(lpdf_ex.shape[0] - 10 * self.__theta.shape[1], 0) - numcutoff = np.minimum(-500, lpdf_ex[thetasort[m_cutoff]]) - if any(lpdf_ex < numcutoff): - rmtheta = totalseen[np.where(lpdf_ex < numcutoff)[0]] - theta = self.__theta[rmtheta, :] - print("removing %d thetas" % rmtheta.shape[0]) - if theta is not None: - nc, c, r = _matrixmatching(theta, self.__theta) - self.__theta = self.__theta[nc, :] - self.__f = self.__f[:, nc] - if self.__options["autofit"]: - self.fit() - return + # default deepcopy for everything else + setattr(new, k, copy.deepcopy(v, memo)) + return new def __optionsset(self, options=None): options = copy.deepcopy(options) @@ -1087,19 +682,6 @@ def mean(self, args=None): else: raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - def mean_gradtheta(self, args=None): - """ - Returns the gradient of the mean at theta and x with respect to theta - when building the prediction. - """ - - pfstr = "predict" # prefix string - opstr = "mean_gradtheta" # operation string - if opstr in self._info.keys(): - return self._info[opstr] - else: - raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - def var(self, args=None): """ Returns the pointwise variance at theta and x when building @@ -1120,161 +702,3 @@ def var(self, args=None): return copy.deepcopy(np.var(self._info["rnd"], 0)) else: raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - - def covx(self, args=None): - """ - Returns the covariance matrix at theta and x when building - the prediction. - """ - - pfstr = "predict" # prefix string - opstr = "covx" # operation string - if (self.emu._emulator__ptf is None) and ( - (pfstr + opstr) in dir(self.emu.method) - ): - if args is None: - args = self.emu._args - return copy.deepcopy(self.emu.method.predictcov(self._info, **args)) - elif opstr in self._info.keys(): - return copy.deepcopy(self._info[opstr]) - elif "covxhalf" in self._info.keys(): - if self._info["covxhalf"].ndim == 2: - return self._info["covxhalf"] @ self._info["covxhalf"].T - else: - am = self._info["covxhalf"].shape - covx = np.ones((am[0], am[1], am[0])) - for k in range(0, self._info["covxhalf"].shape[1]): - A = self._info["covxhalf"][:, k, :] - covx[:, k, :] = A @ A.T - self._info["covx"] = covx - return copy.deepcopy(self._info[opstr]) - else: - raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - - def covxhalf(self, args=None): - """ - Returns the sqrt of the covariance matrix at theta and x when building - the prediction. - That is, if this returns A = predict.covhalf(.)[k], - then A.T @ A = predict.cov(.)[k] - """ - - pfstr = "predict" # prefix string - opstr = "covxhalf" # operation string - if (self.emu._emulator__ptf is None) and ( - (pfstr + opstr) in dir(self.emu.method) - ): - if args is None: - args = self.emu._args - return copy.deepcopy(self.emu.method.predictcov(self._info, **args)) - elif opstr in self._info.keys(): - return copy.deepcopy(self._info[opstr]) - elif "covx" in self._info.keys(): - covxhalf = np.ones(self._info["covx"].shape) - if self._info["covx"].ndim == 2: - W, V = np.linalg.eigh(self._info["covx"]) - covxhalf = V @ (np.sqrt(np.abs(W)) * V.T) - else: - for k in range(0, self._info["covx"].shape[0]): - W, V = np.linalg.eigh(self._info["covx"][k]) - covxhalf[k, :, :] = V @ (np.sqrt(np.abs(W)) * V.T) - self._info["covxhalf"] = covxhalf - return copy.deepcopy(self._info[opstr]) - else: - raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - - def covxhalf_gradtheta(self, args=None): - """ - Returns the gradient of the covxhalf matrix at theta and x when - building the prediction. - """ - - pfstr = "predict" # prefix string - opstr = "covxhalf_gradtheta" # operation string - if opstr in self._info.keys(): - return self._info[opstr] - else: - raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - - def rnd(self, s=100, args=None): - """ - Returns a rnd draws of size s at theta and x - """ - pfstr = "predict" # prefix string - opstr = "rnd" # operation string - - if (self.emu._emulator__ptf is None) and ( - (pfstr + opstr) in dir(self.emu.method) - ): - if args is None: - args = self.emu._args - return copy.deepcopy(self.emu.method.ldf(self._info, **args)) - else: - raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - - def lpdf(self, f=None, args=None): - """ - Returns a log pdf at theta and x - """ - - pfstr = "predict" # prefix string - opstr = "lpdf" # operation string - if (self.emu._emulator__ptf is None) and ( - (pfstr + opstr) in dir(self.emu.method) - ): - if args is None: - args = self.emu._args - return copy.deepcopy(self.emu.method.predictlpdf(self._info, f, **args)) - else: - raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - - def lpdf_gradtheta(self, f=None, args=None): - """ - Returns a log pdf at theta and x - """ - pfstr = "predict" # prefix string - opstr = "lpdf_gradtheta" # operation string - if (self.emu._emulator__ptf is None) and ( - (pfstr + opstr) in dir(self.emu.method) - ): - if args is None: - args = self.emu._args - return copy.deepcopy( - self.emu.method.predictlpdf_gradtheta(self._info, f, **args) - ) - else: - raise ValueError(self.__methodnotfoundstr(pfstr, opstr)) - - -def _matrixmatching(mat1, mat2): - """ - This is an internal function to do matching between two vectors - """ - # This is an internal function to do matching between two vectors - # it just came up alot - # It returns the where each row of mat2 is first found in mat1 - # If a row of mat2 is never found in mat1, then 'nan' is in that location - - if (mat1.shape[0] > (10 ** (4))) or (mat2.shape[0] > (10 ** (4))): - raise ValueError( - "too many matchings attempted." "Don" "t make the method work so hard!" - ) - if mat1.ndim != mat2.ndim: - raise ValueError("Somehow sent non-matching information to" " _matrixmatching") - if mat1.ndim == 1: - matchingmatrix = np.isclose(mat1[:, None].astype("float"), mat2.astype("float")) - else: - matchingmatrix = np.isclose( - mat1[:, 0][:, None].astype("float"), mat2[:, 0].astype("float") - ) - for k in range(1, mat2.shape[1]): - try: - matchingmatrix *= np.isclose( - mat1[:, k][:, None].astype("float"), mat2[:, k].astype("float") - ) - except Exception: - matchingmatrix *= np.equal(mat1[:, k], mat2[:, k]) - r, c = np.where(matchingmatrix.cumsum(axis=0).cumsum(axis=0) == 1) - - nc = np.array(list(set(range(0, mat2.shape[0])) - set(c))).astype("int") - return nc, c, r diff --git a/PUQ/surrogatemethods/PCGP.py b/PUQ/surrogatemethods/PCGP.py deleted file mode 100644 index 96e21db..0000000 --- a/PUQ/surrogatemethods/PCGP.py +++ /dev/null @@ -1,833 +0,0 @@ -"""PCGP (Higdon et al., 2008). """ - -import numpy as np -import scipy.optimize as spo -import scipy.linalg as spla -import copy -from PUQ.surrogatesupport.matern_covmat import covmat as __covmat -import torch - - -def fit( - fitinfo, - x, - theta, - f, - epsilonPC=0.001, - lognugmean=-10, - lognugLB=-20, - varconstant=None, - dampalpha=0.3, - eta=10, - standardpcinfo=None, - verbose=0, - **kwargs -): - """ - The purpose of fit is to take information and plug all of our fit - information into fitinfo, which is a python dictionary. - - Parameters - ---------- - fitinfo : dict - A dictionary including the emulation fitting information once - complete. - The dictionary is passed by reference, so it returns None. - x : numpy.ndarray - An array of inputs. Each row should correspond to a row in f. - theta : numpy.ndarray - An array of parameters. Each row should correspond to a column in f. - f : numpy.ndarray - An array of responses. Each column in f should correspond to a row in - theta. Each row in f should correspond to a row in x. - epsilonPC : scalar - A parameter to control the number of PCs used. The suggested range for - epsilonPC is (0.001, 0.1). The larger epsilonPC is, the fewer PCs will be - used. Note that epsilonPC here is *not* the unexplained variance in - typical principal component analysis. - lognugmean : scalar - A parameter to control the log of the nugget used in fitting the GPs. - The suggested range for lognugmean is (-12, -4). The nugget is estimated, - and this parameter is used to guide the estimation. - lognugLB : scalar - A parameter to control the lower bound of the log of the nugget. The - suggested range for lognugLB is (-24, -12). - varconstant : scalar - A multiplying constant to control the inflation (deflation) of additional - variances if missing values are present. Default is None, the parameter will - be optimized in such case. A general working range is (np.exp(-4), np.exp(4)). - dampalpha : scalar - A parameter to control the rate of increase of variance as amount of missing - values increases. Default is 0.3, otherwise an appropriate range is (0, 0.5). - Values larger than 0.5 are permitted but it leads to poor empirical performance. - eta : scalar - A parameter as an upper bound for the additional variance term. Default is 10. - standardpcinfo : dict - A dictionary user supplies that contains information for standardization of `f`, - in the following format, such that fs = (f - offset) / scale, U are the - orthogonal basis vectors, and S are the singular values from SVD of `fs`. - The entry extravar contains the average squared residual for each column (x). - {'offset': offset, - 'scale': scale, - 'fs': fs, - 'extravar': extravar, - 'U': U, # optional - 'S': S # optional - } - - verbose : scalar - A parameter to suppress in-method console output. Use 0 to suppress output, - use 1 to show output. - - - kwargs : dict, optional - A dictionary containing options. The default is None. - - Returns - ------- - None. - - """ - # print("Fitting...") - f = f.T - fitinfo["epsilonPC"] = epsilonPC - hyp1 = lognugmean - hyp2 = lognugLB - hypvarconst = np.log(varconstant) if varconstant is not None else None - - fitinfo["dampalpha"] = dampalpha - fitinfo["eta"] = eta - - fitinfo["theta"] = theta - fitinfo["f"] = f - fitinfo["x"] = x - - # Standardize the function evaluations f - if standardpcinfo is None: - __standardizef(fitinfo) - else: - fitinfo["standardpcinfo"] = standardpcinfo - - # Construct principal components - __PCs(fitinfo) - numpcs = fitinfo["pc"].shape[1] - - if verbose > 0: - print(fitinfo["method"], "considering ", numpcs, "PCs") - - # Fit emulators for all PCs - emulist = __fitGPs(fitinfo, theta, numpcs, hyp1, hyp2, hypvarconst) - fitinfo["varc_status"] = "fixed" if varconstant is not None else "optimized" - fitinfo["logvarc"] = np.array([emulist[i]["hypvarconst"] for i in range(numpcs)]) - fitinfo["pcstdvar"] = np.exp(fitinfo["logvarc"]) * fitinfo["unscaled_pcstdvar"] - fitinfo["emulist"] = emulist - - return - - -def update(fitinfo, x, theta, f, **kwargs): - # print("Updating...") - f = f.T - # print(fitinfo['f'].shape) - # print(f.shape) - # print(fitinfo['pc'].shape) - - fitinfo["theta"] = theta - fitinfo["f"] = f - fitinfo["x"] = x - - standardpcinfo = fitinfo["standardpcinfo"] - offset = standardpcinfo["offset"] - scale = standardpcinfo["scale"] - fs = (f - offset) / scale - standardpcinfo["fs"] = fs - fitinfo["pc"] = fs @ fitinfo["pct"] - emulist = fitinfo["emulist"] - numpcs = fitinfo["pc"].shape[1] - - for pcanum in range(0, numpcs): - subinfo = emulist[pcanum] - R = __covmat(theta, theta, subinfo["hypcov"]) - subinfo["R"] = (1 - subinfo["nug"]) * R + subinfo["nug"] * np.eye(R.shape[0]) - W, V = np.linalg.eigh(subinfo["R"]) - Vh = V / np.sqrt(np.abs(W)) - # sig2ofconst = subinfo['sig2ofconst'] - g = fitinfo["pc"][:, pcanum] - # fcenter = Vh.T @ g - subinfo["Vh"] = Vh - # n = subinfo['R'].shape[0] - # subinfo['sig2'] = (np.mean(fcenter ** 2) * n + sig2ofconst) / (n + sig2ofconst) - subinfo["Rinv"] = V @ np.diag(1 / W) @ V.T - subinfo["pw"] = subinfo["Rinv"] @ g - - -def predict(predinfo, fitinfo, x, theta, **kwargs): - r""" - Finds prediction at theta and x given the dictionary fitinfo. - This [emulationpredictdocstring] automatically filled by docinfo.py when - running updatedocs.py - - Parameters - ---------- - predinfo : dict - An arbitary dictionary where you should place all of your prediction - information once complete. This dictionary is pass by reference, so - there is no reason to return anything. Keep only stuff that will be - used by predict. Key elements are - - - `predinfo['mean']` : `predinfo['mean'][k]` is mean of the prediction - at all x at `theta[k]`. - - `predinfo['var']` : `predinfo['var'][k]` is variance of the - prediction at all x at `theta[k]`. - - `predinfo['cov']` : `predinfo['cov'][k]` is covariance matrix of the prediction - at all x at `theta[k]`. - - `predinfo['covhalf']` : if `A = predinfo['covhalf'][k]` then - `A.T @ A = predinfo['cov'][k]`. - - fitinfo : dict - An arbitary dictionary where you placed all your important fitting - information from the fit function above. - - x : array of objects - An matrix (vector) of inputs for prediction. - - theta : array of objects - An matrix (vector) of parameters to prediction. - - kwargs : dict - A dictionary containing additional options - """ - return_grad = False - if ( - (kwargs is not None) - and ("return_grad" in kwargs.keys()) - and (kwargs["return_grad"] is True) - ): - return_grad = True - return_covx = True - if ( - (kwargs is not None) - and ("return_covx" in kwargs.keys()) - and (kwargs["return_covx"] is False) - ): - return_covx = False - infos = fitinfo["emulist"] - predvecs = np.zeros((theta.shape[0], len(infos))) - predvars = np.zeros((theta.shape[0], len(infos))) - - if return_grad: - predvecs_gradtheta = np.zeros((theta.shape[0], len(infos), theta.shape[1])) - predvars_gradtheta = np.zeros((theta.shape[0], len(infos), theta.shape[1])) - drsave = np.array(np.ones(len(infos)), dtype=object) - if predvecs.ndim < 1.5: - predvecs = predvecs.reshape((1, -1)) - predvars = predvars.reshape((1, -1)) - try: - if ( - x is None - or np.all(np.equal(x, fitinfo["x"])) - or np.allclose(x, fitinfo["x"]) - ): - xind = np.arange(0, x.shape[0]) - xnewind = np.arange(0, x.shape[0]) - else: - raise - except Exception: - matchingmatrix = np.ones((x.shape[0], fitinfo["x"].shape[0])) - for k in range(0, x[0].shape[0]): - try: - matchingmatrix *= np.isclose(x[:, k][:, None], fitinfo["x"][:, k]) - except Exception: - matchingmatrix *= np.equal(x[:, k][:, None], fitinfo["x"][:, k]) - xind = np.argwhere(matchingmatrix > 0.5)[:, 1] - xnewind = np.argwhere(matchingmatrix > 0.5)[:, 0] - - rsave = np.array(np.ones(len(infos)), dtype=object) - - # loop over principal components - for k in range(0, len(infos)): - if infos[k]["hypind"] == k: - # covariance matrix between new theta and thetas from fit. - if return_grad: - rsave[k], drsave[k] = __covmat( - theta, fitinfo["theta"], infos[k]["hypcov"], return_gradx1=True - ) - else: - rsave[k] = __covmat(theta, fitinfo["theta"], infos[k]["hypcov"]) - # adjusted covariance matrix - r = (1 - infos[k]["nug"]) * np.squeeze(rsave[infos[k]["hypind"]]) - - try: - rVh = r @ infos[k]["Vh"] - rVh2 = rVh @ (infos[k]["Vh"]).T - except Exception: - for i in range(0, len(infos)): - print((i, infos[i]["hypind"])) - raise ValueError("Something went wrong with fitted components") - - if rVh.ndim < 1.5: - rVh = rVh.reshape((1, -1)) - if rVh2.ndim < 1.5: - rVh2 = np.reshape(rVh2, (1, -1)) - predvecs[:, k] = r @ infos[k]["pw"] - if return_grad: - drsave_hypind = np.squeeze(drsave[infos[k]["hypind"]]) - if drsave_hypind.ndim < 2.5 and theta.shape[1] < 1.5: - drsave_hypind = np.reshape(drsave_hypind, (*drsave_hypind.shape, 1)) - elif drsave_hypind.ndim < 2.5 and theta.shape[1] > 1.5: - drsave_hypind = np.reshape(drsave_hypind, (1, *drsave_hypind.shape)) - - dr = (1 - infos[k]["nug"]) * drsave_hypind - if dr.ndim == 2: - drVh = dr.T @ infos[k]["Vh"] - predvecs_gradtheta[:, k, :] = dr.T @ infos[k]["pw"] - predvars_gradtheta[:, k, :] = ( - -infos[k]["sig2"] * 2 * np.sum(rVh * drVh, 1) - ) - else: - drpw = np.squeeze(dr.transpose(0, 2, 1) @ infos[k]["pw"]) - if drpw.ndim < 1.5 and theta.shape[1] < 1.5: - drpw = np.reshape(drpw, (-1, 1)) - elif drpw.ndim < 1.5 and theta.shape[1] > 1.5: - drpw = np.reshape(drpw, (1, -1)) - - predvecs_gradtheta[:, k, :] = (1 - infos[k]["nug"]) * drpw - predvars_gradtheta[:, k, :] = -(infos[k]["sig2"] * 2) * np.einsum( - "ij,ijk->ik", rVh2, dr - ) - predvars[:, k] = infos[k]["sig2"] * np.abs(1 - np.sum(rVh**2, 1)) - - # calculate predictive mean and variance - predinfo["mean"] = np.full((x.shape[0], theta.shape[0]), np.nan) - predinfo["var"] = np.full((x.shape[0], theta.shape[0]), np.nan) - pctscale = (fitinfo["pcti"].T * fitinfo["standardpcinfo"]["scale"]).T - # pctscale = (fitinfo['pct'].T * fitinfo['standardpcinfo']['scale']).T - predinfo["mean"][xnewind, :] = ( - (predvecs @ pctscale[xind, :].T) + fitinfo["standardpcinfo"]["offset"][xind] - ).T - predinfo["var"][xnewind, :] = ( - ( - fitinfo["standardpcinfo"]["extravar"][xind] - + predvars @ (pctscale[xind, :] ** 2).T - ) - ).T - - predinfo["extravar"] = 1 * fitinfo["standardpcinfo"]["extravar"][xind] - predinfo["predvars"] = 1 * predvars - predinfo["predvecs"] = 1 * predvecs - predinfo["phi"] = 1 * pctscale[xind, :] - - if return_covx: - CH = np.sqrt(predvars)[:, :, None] * (pctscale[xind, :].T)[None, :, :] - predinfo["covxhalf"] = np.full( - (theta.shape[0], CH.shape[1], x.shape[0]), np.nan - ) - predinfo["covxhalf"][:, :, xnewind] = CH - predinfo["covxhalf"] = predinfo["covxhalf"].transpose((2, 0, 1)) - - if return_grad: - predinfo["mean_gradtheta"] = np.full( - (x.shape[0], theta.shape[0], theta.shape[1]), np.nan - ) - predinfo["mean_gradtheta"][xnewind, :, :] = ( - (predvecs_gradtheta.transpose(0, 2, 1) @ pctscale[xind, :].T) - ).transpose((2, 0, 1)) - predinfo["predvars_gradtheta"] = 1 * predvars_gradtheta - predinfo["predvecs_gradtheta"] = 1 * predvecs_gradtheta - - if return_covx: - - dsqrtpredvars = 0.5 * ( - predvars_gradtheta.transpose(2, 0, 1) / np.sqrt(predvars) - ).transpose(1, 2, 0) - - if np.allclose(xnewind, xind): - predinfo["covxhalf_gradtheta"] = ( - dsqrtpredvars.transpose(2, 0, 1)[:, :, :, None] - * (pctscale[xind, :].T)[None, :, :] - ).transpose(3, 1, 2, 0) - else: - predinfo["covxhalf_gradtheta"] = np.full( - (x.shape[0], theta.shape[0], CH.shape[1], theta.shape[1]), np.nan - ) - predinfo["covxhalf_gradtheta"][xnewind] = ( - dsqrtpredvars.transpose(2, 0, 1)[:, :, :, None] - * (pctscale[xind, :].T)[None, :, :] - ).transpose(3, 1, 2, 0) - return - - -def acquisition(fitinfo, x, theta1, theta2, **kwargs): - - infos = fitinfo["emulist"] - predcov = np.zeros((theta1.shape[0], theta2.shape[0], len(infos))) - tau = np.zeros((theta1.shape[0], theta2.shape[0], len(infos))) - predvars_2 = np.zeros((theta2.shape[0], len(infos))) - try: - if ( - x is None - or np.all(np.equal(x, fitinfo["x"])) - or np.allclose(x, fitinfo["x"]) - ): - xind = np.arange(0, x.shape[0]) - xnewind = np.arange(0, x.shape[0]) - else: - raise - except Exception: - matchingmatrix = np.ones((x.shape[0], fitinfo["x"].shape[0])) - for k in range(0, x[0].shape[0]): - try: - matchingmatrix *= np.isclose(x[:, k][:, None], fitinfo["x"][:, k]) - except Exception: - matchingmatrix *= np.equal(x[:, k][:, None], fitinfo["x"][:, k]) - xind = np.argwhere(matchingmatrix > 0.5)[:, 1] - xnewind = np.argwhere(matchingmatrix > 0.5)[:, 0] - - rsave_1 = np.array(np.ones(len(infos)), dtype=object) - rsave_2 = np.array(np.ones(len(infos)), dtype=object) - rsave_3 = np.array(np.ones(len(infos)), dtype=object) - # loop over principal components - for k in range(0, len(infos)): - if infos[k]["hypind"] == k: - # covariance matrix between new theta and thetas from fit. - rsave_1[k] = __covmat(theta1, fitinfo["theta"], infos[k]["hypcov"]) - rsave_2[k] = __covmat(theta2, fitinfo["theta"], infos[k]["hypcov"]) - rsave_3[k] = __covmat(theta1, theta2, infos[k]["hypcov"]) - - # adjusted covariance matrix - r_1 = (1 - infos[k]["nug"]) * np.squeeze(rsave_1[infos[k]["hypind"]]) - r_2 = (1 - infos[k]["nug"]) * np.squeeze(rsave_2[infos[k]["hypind"]]) - r_3 = (1 - infos[k]["nug"]) * np.squeeze(rsave_3[infos[k]["hypind"]]) - - # print(theta1.shape) - # print(theta2.shape) - - # print('r1:', r_1.shape) - # print('r2:', r_2.shape) - # print('r3:', r_3.shape) - try: - rVh_1 = r_1 @ infos[k]["Vh"] - rVh_2 = r_2 @ infos[k]["Vh"] - except Exception: - for i in range(0, len(infos)): - print((i, infos[i]["hypind"])) - raise ValueError("Something went wrong with fitted components") - - if rVh_1.ndim < 1.5: - rVh_1 = rVh_1.reshape((1, -1)) - if rVh_2.ndim < 1.5: - rVh_2 = rVh_2.reshape((1, -1)) - - # print('rVh1:', rVh_1.shape) - # print('rVh2:', rVh_2.shape) - - # predvars_1[:, k] = infos[k]['sig2'] * np.abs(1 - np.sum(rVh_1 ** 2, 1)) - - # predcov[:, :, k] = infos[k]['sig2'] * (r_3.reshape((theta1.shape[0], 1)) - rVh_1 @ rVh_2.T) - predcov[:, :, k] = infos[k]["sig2"] * ( - r_3.reshape((theta1.shape[0], theta2.shape[0])) - rVh_1 @ rVh_2.T - ) - # print('predcov:', predcov.shape) - - predvars_2[:, k] = infos[k]["sig2"] * np.abs(1 - np.sum(rVh_2**2, 1)) - predvars_2[:, k] += infos[k]["nug"] - - # print('predvars_2:', predvars_2.shape) - - tau[:, :, k] = (predcov[:, :, k] ** 2) / predvars_2[:, k] - # print('tau:', tau.shape) - pctscale = (fitinfo["pcti"].T * fitinfo["standardpcinfo"]["scale"]).T - - # print('here1') - - BtauT = np.sqrt(tau)[:, :, :, None] * (pctscale.T) - # print(BtauT.shape) - Btau = BtauT.transpose(0, 1, 3, 2) - Btau_torch = torch.from_numpy(Btau) - Btau_torchT = torch.from_numpy(BtauT) - phi_mat_torch = torch.matmul(Btau_torch, Btau_torchT) - phi_mat_np = phi_mat_torch.numpy() - phi_mat_np = phi_mat_np.transpose(0, 3, 2, 1) - - # print('here2') - # print(phi_mat_np[1, :, :, 0]) - # phi_matrix2 = np.zeros((theta1.shape[0], len(x), len(x), theta2.shape[0])) - # for i in range(theta1.shape[0]): - # for j in range(theta2.shape[0]): - # phi_matrix2[i, :, :, j] = pctscale@np.diag(tau[i, j, :])@pctscale.T - # phi_matrix[i, :, :, j] = Btau[i, j, :, :]@Btau[i, j, :, :].T - - # print(phi_matrix[1, :, :, 0]) - # print(phi_matrix2[1, :, :, 0]) - return phi_mat_np # phi_matrix - - -def predictlpdf(predinfo, f, return_grad=False, addvar=0, **kwargs): - totvar = addvar + predinfo["extravar"] - rf = ((f.T - predinfo["mean"].T) * (1 / np.sqrt(totvar))).T - Gf = predinfo["phi"].T * (1 / np.sqrt(totvar)) - Gfrf = Gf @ rf - Gf2 = Gf @ Gf.T - likv = np.sum(rf**2, 0) - if return_grad: - rf2 = -( - predinfo["mean_gradtheta"].transpose(2, 1, 0) * (1 / np.sqrt(totvar)) - ).transpose(2, 1, 0) - Gfrf2 = (Gf @ rf2.transpose(1, 0, 2)).transpose(1, 0, 2) - dlikv = 2 * np.sum(rf2.transpose(2, 1, 0) * rf.transpose(1, 0), 2).T - for c in range(0, predinfo["predvars"].shape[0]): - w, v = np.linalg.eig(np.diag(1 / (predinfo["predvars"][c, :])) + Gf2) - term1 = (v * (1 / w)) @ (v.T @ Gfrf[:, c]) - - likv[c] -= Gfrf[:, c].T @ term1 - likv[c] += np.sum(np.log(predinfo["predvars"][c, :])) - likv[c] += np.sum(np.log(w)) - if return_grad: - Si = (v * (1 / w)) @ v.T - grt = ( - predinfo["predvars_gradtheta"][c, :, :].T / predinfo["predvars"][c, :] - ).T - dlikv[c, :] += np.sum(grt, 0) - grt = (-grt.T / predinfo["predvars"][c, :]).T - dlikv[c, :] += np.diag(Si) @ grt - term2 = (term1 / predinfo["predvars"][c, :]) ** 2 - dlikv[c, :] -= 2 * Gfrf2[:, c, :].T @ term1 - dlikv[c, :] -= term2 @ (predinfo["predvars_gradtheta"][c, :, :]) - if return_grad: - return (-likv / 2).reshape(-1, 1), (-dlikv / 2) - else: - return (-likv / 2).reshape(-1, 1) - - -def __standardizef(fitinfo, offset=None, scale=None): - r"""Standardizes f by creating offset, scale and fs.""" - # Extracting from input dictionary - f = fitinfo["f"] - epsilonPC = fitinfo["epsilonPC"] - - if (offset is not None) and (scale is not None): - if offset.shape[0] == f.shape[1] and scale.shape[0] == f.shape[1]: - if np.any(np.nanmean(np.abs(f - offset) / scale, 1) > 4): - offset = None - scale = None - else: - offset = None - scale = None - if offset is None or scale is None: - offset = np.zeros(f.shape[1]) - scale = np.zeros(f.shape[1]) - for k in range(0, f.shape[1]): - offset[k] = np.nanmean(f[:, k]) - scale[k] = np.nanstd(f[:, k]) / np.sqrt(1 - np.isnan(f[:, k]).mean()) - if scale[k] == 0: - raise ValueError("You have a row that is non-varying.") - - fs = np.zeros(f.shape) - fs = (f - offset) / scale - - # Assigning new values to the dictionary - U, S, _ = np.linalg.svd(fs.T, full_matrices=False) - Sp = S**2 - epsilonPC - Up = U[:, Sp > 0] - - extravar = np.nanmean((fs - fs @ Up @ Up.T) ** 2, 0) * (scale**2) - - standardpcinfo = { - "offset": offset, - "scale": scale, - "fs": fs, - "U": U, - "S": S, - "extravar": extravar, - } - - fitinfo["standardpcinfo"] = standardpcinfo - return - - -def __PCs(fitinfo): - "Apply PCA to reduce the dimension of `f`." - # Extracting from input dictionary - f = fitinfo["f"] - epsilonPC = fitinfo["epsilonPC"] - fs = fitinfo["standardpcinfo"]["fs"] - - if "U" in fitinfo["standardpcinfo"]: - U = fitinfo["standardpcinfo"]["U"] - S = fitinfo["standardpcinfo"]["S"] - else: - U, S, _ = np.linalg.svd(fs.T, full_matrices=False) - Sp = S**2 - epsilonPC - pct = U[:, Sp > 0] - pcw = np.sqrt(Sp[Sp > 0]) - - pcstdvar = np.zeros((f.shape[0], pct.shape[1])) - fitinfo["pcw"] = pcw - fitinfo["pcto"] = 1 * pct - effn = np.sum(np.clip(1 - pcstdvar, 0, 1)) - fitinfo["pct"] = pct * pcw / np.sqrt(effn) - fitinfo["pcti"] = pct * (np.sqrt(effn) / pcw) - # fitinfo['pc'] = pc * (np.sqrt(effn) / pcw) - fitinfo["pc"] = fs @ fitinfo["pct"] - fitinfo["unscaled_pcstdvar"] = pcstdvar - return - - -def __fitGPs(fitinfo, theta, numpcs, hyp1, hyp2, varconstant): - """Fit emulators for all principle components.""" - if "emulist" in fitinfo.keys(): - hypstarts = np.zeros((numpcs, fitinfo["emulist"][0]["hyp"].shape[0])) - hypinds = -1 * np.ones(numpcs) - for pcanum in range(0, min(numpcs, len(fitinfo["emulist"]))): - hypstarts[pcanum, :] = fitinfo["emulist"][pcanum]["hyp"] - hypinds[pcanum] = fitinfo["emulist"][pcanum]["hypind"] - else: - hypstarts = None - hypinds = -1 * np.ones(numpcs) - - emulist = [dict() for x in range(0, numpcs)] - for iters in range(0, 3): - for pcanum in range(0, numpcs): - if np.sum(hypinds == np.array(range(0, numpcs))) > 0.5: - hypwhere = np.where(hypinds == np.array(range(0, numpcs)))[0] - emulist[pcanum] = __fitGP1d( - theta=theta, - g=fitinfo["pc"][:, pcanum], - hyp1=hyp1, - hyp2=hyp2, - hypvarconst=varconstant, - gvar=fitinfo["unscaled_pcstdvar"][:, pcanum], - dampalpha=fitinfo["dampalpha"], - eta=fitinfo["eta"], - hypstarts=hypstarts[hypwhere, :], - hypinds=hypwhere, - sig2ofconst=0.01, - ) - else: - emulist[pcanum] = __fitGP1d( - theta=theta, - g=fitinfo["pc"][:, pcanum], - hyp1=hyp1, - hyp2=hyp2, - hypvarconst=varconstant, - gvar=fitinfo["unscaled_pcstdvar"][:, pcanum], - dampalpha=fitinfo["dampalpha"], - eta=fitinfo["eta"], - sig2ofconst=0.01, - ) - hypstarts = np.zeros((numpcs, emulist[pcanum]["hyp"].shape[0])) - emulist[pcanum]["hypind"] = min(pcanum, emulist[pcanum]["hypind"]) - hypstarts[pcanum, :] = emulist[pcanum]["hyp"] - if emulist[pcanum]["hypind"] < -0.5: - emulist[pcanum]["hypind"] = 1 * pcanum - hypinds[pcanum] = 1 * emulist[pcanum]["hypind"] - return emulist - - -def __fitGP1d( - theta, - g, - hyp1, - hyp2, - hypvarconst, - gvar=None, - dampalpha=None, - eta=None, - hypstarts=None, - hypinds=None, - sig2ofconst=None, -): - """Return a fitted model from the emulator model using smart method.""" - hypvarconstmean = 4 if hypvarconst is None else hypvarconst - hypvarconstLB = -8 if hypvarconst is None else hypvarconst - 0.5 - hypvarconstUB = 8 if hypvarconst is None else hypvarconst + 0.5 - - subinfo = {} - subinfo["hypregmean"] = np.append( - 0 + 0.5 * np.log(theta.shape[1]) + np.log(np.std(theta, 0)), - (0, hypvarconstmean, hyp1), - ) - subinfo["hypregLB"] = np.append( - -4 + 0.5 * np.log(theta.shape[1]) + np.log(np.std(theta, 0)), - (-12, hypvarconstLB, hyp2), - ) - - subinfo["hypregUB"] = np.append( - 4 + 0.5 * np.log(theta.shape[1]) + np.log(np.std(theta, 0)), - (2, hypvarconstUB, -8), - ) - subinfo["hypregstd"] = (subinfo["hypregUB"] - subinfo["hypregLB"]) / 8 - subinfo["hypregstd"][-3] = 2 - subinfo["hypregstd"][-1] = 4 - subinfo["hyp"] = 1 * subinfo["hypregmean"] - nhyptrain = np.max(np.min((20 * theta.shape[1], theta.shape[0]))) - if theta.shape[0] > nhyptrain: - thetac = np.random.choice(theta.shape[0], nhyptrain, replace=False) - else: - thetac = range(0, theta.shape[0]) - subinfo["theta"] = theta[thetac, :] - subinfo["g"] = g[thetac] - - # maxgvar = np.max(gvar) - # gvar = gvar / ((np.abs(maxgvar*1.001 - gvar)) ** dampalpha) - - gvar = np.minimum(eta, gvar / ((1 - gvar) ** dampalpha)) - - # print(gvar) - subinfo["sig2ofconst"] = sig2ofconst - subinfo["gvar"] = gvar[thetac] - hypind0 = -1 - - L0 = __negloglik(subinfo["hyp"], subinfo) - if hypstarts is not None: - L0 = __negloglik(subinfo["hyp"], subinfo) - for k in range(0, hypstarts.shape[0]): - L1 = __negloglik(hypstarts[k, :], subinfo) - if L1 < L0: - subinfo["hyp"] = hypstarts[k, :] - L0 = 1 * L1 - hypind0 = hypinds[k] - - if hypind0 > -0.5 and hypstarts.ndim > 1: - dL = __negloglikgrad(subinfo["hyp"], subinfo) - scalL = np.std(hypstarts, 0) * hypstarts.shape[0] / (1 + hypstarts.shape[0]) + ( - 1 / (1 + hypstarts.shape[0]) * subinfo["hypregstd"] - ) - if np.sum((dL * scalL) ** 2) < 1.25 * ( - subinfo["hyp"].shape[0] + 5 * np.sqrt(subinfo["hyp"].shape[0]) - ): - skipop = True - else: - skipop = False - else: - skipop = False - - if not skipop: - - def scaledlik(hypv): - hyprs = subinfo["hypregmean"] + hypv * subinfo["hypregstd"] - return __negloglik(hyprs, subinfo) - - def scaledlikgrad(hypv): - hyprs = subinfo["hypregmean"] + hypv * subinfo["hypregstd"] - return __negloglikgrad(hyprs, subinfo) * subinfo["hypregstd"] - - newLB = (subinfo["hypregLB"] - subinfo["hypregmean"]) / subinfo["hypregstd"] - newUB = (subinfo["hypregUB"] - subinfo["hypregmean"]) / subinfo["hypregstd"] - - newhyp0 = (subinfo["hyp"] - subinfo["hypregmean"]) / subinfo["hypregstd"] - - opval = spo.minimize( - scaledlik, - newhyp0, - method="L-BFGS-B", - options={"gtol": 0.1}, - jac=scaledlikgrad, - bounds=spo.Bounds(newLB, newUB), - ) - - hypn = subinfo["hypregmean"] + opval.x * subinfo["hypregstd"] - likdiff = L0 - __negloglik(hypn, subinfo) - else: - likdiff = 0 - if hypind0 > -0.5 and (2 * likdiff) < 1.25 * ( - subinfo["hyp"].shape[0] + 5 * np.sqrt(subinfo["hyp"].shape[0]) - ): - subinfo["hypcov"] = subinfo["hyp"][:-2] - subinfo["hypvarconst"] = subinfo["hyp"][-2] - subinfo["hypind"] = hypind0 - subinfo["nug"] = np.exp(subinfo["hyp"][-1]) / (1 + np.exp(subinfo["hyp"][-1])) - - R = __covmat(theta, theta, subinfo["hypcov"]) - - subinfo["R"] = (1 - subinfo["nug"]) * R + subinfo["nug"] * np.eye(R.shape[0]) - if gvar is not None: - subinfo["R"] += np.exp(subinfo["hypvarconst"]) * np.diag(gvar) - - W, V = np.linalg.eigh(subinfo["R"]) - Vh = V / np.sqrt(np.abs(W)) - fcenter = Vh.T @ g - subinfo["Vh"] = Vh - n = subinfo["R"].shape[0] - subinfo["sig2"] = (np.mean(fcenter**2) * n + sig2ofconst) / (n + sig2ofconst) - subinfo["Rinv"] = V @ np.diag(1 / W) @ V.T - else: - subinfo["hyp"] = hypn - subinfo["hypind"] = -1 - subinfo["hypcov"] = subinfo["hyp"][:-2] - subinfo["hypvarconst"] = subinfo["hyp"][-2] - subinfo["nug"] = np.exp(subinfo["hyp"][-1]) / (1 + np.exp(subinfo["hyp"][-1])) - - R = __covmat(theta, theta, subinfo["hypcov"]) - subinfo["R"] = (1 - subinfo["nug"]) * R + subinfo["nug"] * np.eye(R.shape[0]) - if gvar is not None: - subinfo["R"] += np.exp(subinfo["hypvarconst"]) * np.diag(gvar) - n = subinfo["R"].shape[0] - W, V = np.linalg.eigh(subinfo["R"]) - Vh = V / np.sqrt(np.abs(W)) - fcenter = Vh.T @ g - subinfo["sig2"] = (np.mean(fcenter**2) * n + sig2ofconst) / (n + sig2ofconst) - subinfo["Rinv"] = Vh @ Vh.T - subinfo["Vh"] = Vh - subinfo["pw"] = subinfo["Rinv"] @ g - return subinfo - - -def __negloglik(hyp, info): - """Return penalized log likelihood of single demensional GP model.""" - R0 = __covmat(info["theta"], info["theta"], hyp[:-2]) - nug = np.exp(hyp[-1]) / (1 + np.exp(hyp[-1])) - R = (1 - nug) * R0 + nug * np.eye(info["theta"].shape[0]) - - if info["gvar"] is not None: - R += np.exp(hyp[-2]) * np.diag(info["gvar"]) - W, V = np.linalg.eigh(R) - Vh = V / np.sqrt(np.abs(W)) - fcenter = Vh.T @ info["g"] - n = info["g"].shape[0] - - sig2ofconst = info["sig2ofconst"] - sig2hat = (n * np.mean(fcenter**2) + sig2ofconst) / (n + sig2ofconst) - negloglik = 1 / 2 * np.sum(np.log(np.abs(W))) + 1 / 2 * n * np.log(sig2hat) - negloglik += 0.5 * np.sum( - ((10 ** (-8) + hyp - info["hypregmean"]) / (info["hypregstd"])) ** 2 - ) - return negloglik - - -def __negloglikgrad(hyp, info): - """Return gradient of the penalized log likelihood of single demensional - GP model.""" - R0, dR = __covmat(info["theta"], info["theta"], hyp[:-2], True) - nug = np.exp(hyp[-1]) / (1 + np.exp(hyp[-1])) - R = (1 - nug) * R0 + nug * np.eye(info["theta"].shape[0]) - dR = (1 - nug) * dR - dRappend2 = nug / (1 + np.exp(hyp[-1])) * (-R0 + np.eye(info["theta"].shape[0])) - - if info["gvar"] is not None: - R += np.exp(hyp[-2]) * np.diag(info["gvar"]) - dRappend1 = np.exp(hyp[-2]) * np.diag(info["gvar"]) - else: - dRappend1 = 0 * np.eye(info["theta"].shape[0]) - - dR = np.append(dR, dRappend1[:, :, None], axis=2) - dR = np.append(dR, dRappend2[:, :, None], axis=2) - W, V = np.linalg.eigh(R) - Vh = V / np.sqrt(np.abs(W)) - fcenter = Vh.T @ info["g"] - n = info["g"].shape[0] - - sig2ofconst = info["sig2ofconst"] - sig2hat = (n * np.mean(fcenter**2) + sig2ofconst) / (n + sig2ofconst) - dnegloglik = np.zeros(dR.shape[2]) - Rinv = Vh @ Vh.T - - for k in range(0, dR.shape[2]): - dsig2hat = -np.sum( - (Vh @ np.multiply.outer(fcenter, fcenter) @ Vh.T) * dR[:, :, k] - ) / (n + sig2ofconst) - dnegloglik[k] += 0.5 * n * dsig2hat / sig2hat - dnegloglik[k] += 0.5 * np.sum(Rinv * dR[:, :, k]) - - dnegloglik += (10 ** (-8) + hyp - info["hypregmean"]) / ((info["hypregstd"]) ** 2) - return dnegloglik diff --git a/PUQ/surrogatemethods/PCGPexp.py b/PUQ/surrogatemethods/PCGPexp.py deleted file mode 100644 index c28da48..0000000 --- a/PUQ/surrogatemethods/PCGPexp.py +++ /dev/null @@ -1,1059 +0,0 @@ -"""PCGP (Higdon et al., 2008). """ - -import numpy as np -import scipy.optimize as spo -import scipy.linalg as spla -import copy -from PUQ.surrogatesupport.matern_covmat import covmat as __covmat -import torch -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - multiple_pdfs, - multiple_determinants, -) - - -def fit( - fitinfo, - x, - theta, - f, - epsilonPC=0.001, - lognugmean=-10, - lognugLB=-20, - varconstant=None, - dampalpha=0.3, - eta=10, - standardpcinfo=None, - verbose=0, - **kwargs -): - """ - The purpose of fit is to take information and plug all of our fit - information into fitinfo, which is a python dictionary. - - Parameters - ---------- - fitinfo : dict - A dictionary including the emulation fitting information once - complete. - The dictionary is passed by reference, so it returns None. - x : numpy.ndarray - An array of inputs. Each row should correspond to a row in f. - theta : numpy.ndarray - An array of parameters. Each row should correspond to a column in f. - f : numpy.ndarray - An array of responses. Each column in f should correspond to a row in - theta. Each row in f should correspond to a row in x. - epsilonPC : scalar - A parameter to control the number of PCs used. The suggested range for - epsilonPC is (0.001, 0.1). The larger epsilonPC is, the fewer PCs will be - used. Note that epsilonPC here is *not* the unexplained variance in - typical principal component analysis. - lognugmean : scalar - A parameter to control the log of the nugget used in fitting the GPs. - The suggested range for lognugmean is (-12, -4). The nugget is estimated, - and this parameter is used to guide the estimation. - lognugLB : scalar - A parameter to control the lower bound of the log of the nugget. The - suggested range for lognugLB is (-24, -12). - varconstant : scalar - A multiplying constant to control the inflation (deflation) of additional - variances if missing values are present. Default is None, the parameter will - be optimized in such case. A general working range is (np.exp(-4), np.exp(4)). - dampalpha : scalar - A parameter to control the rate of increase of variance as amount of missing - values increases. Default is 0.3, otherwise an appropriate range is (0, 0.5). - Values larger than 0.5 are permitted but it leads to poor empirical performance. - eta : scalar - A parameter as an upper bound for the additional variance term. Default is 10. - standardpcinfo : dict - A dictionary user supplies that contains information for standardization of `f`, - in the following format, such that fs = (f - offset) / scale, U are the - orthogonal basis vectors, and S are the singular values from SVD of `fs`. - The entry extravar contains the average squared residual for each column (x). - {'offset': offset, - 'scale': scale, - 'fs': fs, - 'extravar': extravar, - 'U': U, # optional - 'S': S # optional - } - - verbose : scalar - A parameter to suppress in-method console output. Use 0 to suppress output, - use 1 to show output. - - - kwargs : dict, optional - A dictionary containing options. The default is None. - - Returns - ------- - None. - - """ - # print('Fitting...') - f = f.T - fitinfo["epsilonPC"] = epsilonPC - hyp1 = lognugmean - hyp2 = lognugLB - hypvarconst = np.log(varconstant) if varconstant is not None else None - - fitinfo["dampalpha"] = dampalpha - fitinfo["eta"] = eta - - fitinfo["theta"] = theta - fitinfo["f"] = f - fitinfo["x"] = x - - # Standardize the function evaluations f - if standardpcinfo is None: - __standardizef(fitinfo) - else: - fitinfo["standardpcinfo"] = standardpcinfo - - # Construct principal components - __PCs(fitinfo) - numpcs = fitinfo["pc"].shape[1] - - if verbose > 0: - print(fitinfo["method"], "considering ", numpcs, "PCs") - - # Fit emulators for all PCs - emulist = __fitGPs(fitinfo, theta, numpcs, hyp1, hyp2, hypvarconst) - fitinfo["varc_status"] = "fixed" if varconstant is not None else "optimized" - fitinfo["logvarc"] = np.array([emulist[i]["hypvarconst"] for i in range(numpcs)]) - fitinfo["pcstdvar"] = np.exp(fitinfo["logvarc"]) * fitinfo["unscaled_pcstdvar"] - fitinfo["emulist"] = emulist - - return - - -def update(fitinfo, x, theta, f, **kwargs): - print("Updating...") - f = f.T - # print(fitinfo['f'].shape) - # print(f.shape) - # print(fitinfo['pc'].shape) - - fitinfo["theta"] = theta - fitinfo["f"] = f - fitinfo["x"] = x - - standardpcinfo = fitinfo["standardpcinfo"] - offset = standardpcinfo["offset"] - scale = standardpcinfo["scale"] - fs = (f - offset) / scale - standardpcinfo["fs"] = fs - fitinfo["pc"] = fs @ fitinfo["pct"] - emulist = fitinfo["emulist"] - numpcs = fitinfo["pc"].shape[1] - - for pcanum in range(0, numpcs): - subinfo = emulist[pcanum] - R = __covmat(theta, theta, subinfo["hypcov"]) - subinfo["R"] = (1 - subinfo["nug"]) * R + subinfo["nug"] * np.eye(R.shape[0]) - W, V = np.linalg.eigh(subinfo["R"]) - Vh = V / np.sqrt(np.abs(W)) - # sig2ofconst = subinfo['sig2ofconst'] - g = fitinfo["pc"][:, pcanum] - # fcenter = Vh.T @ g - subinfo["Vh"] = Vh - # n = subinfo['R'].shape[0] - # subinfo['sig2'] = (np.mean(fcenter ** 2) * n + sig2ofconst) / (n + sig2ofconst) - subinfo["Rinv"] = V @ np.diag(1 / W) @ V.T - subinfo["pw"] = subinfo["Rinv"] @ g - - -def predict(predinfo, fitinfo, x, theta, **kwargs): - r""" - Finds prediction at theta and x given the dictionary fitinfo. - This [emulationpredictdocstring] automatically filled by docinfo.py when - running updatedocs.py - - Parameters - ---------- - predinfo : dict - An arbitary dictionary where you should place all of your prediction - information once complete. This dictionary is pass by reference, so - there is no reason to return anything. Keep only stuff that will be - used by predict. Key elements are - - - `predinfo['mean']` : `predinfo['mean'][k]` is mean of the prediction - at all x at `theta[k]`. - - `predinfo['var']` : `predinfo['var'][k]` is variance of the - prediction at all x at `theta[k]`. - - `predinfo['cov']` : `predinfo['cov'][k]` is covariance matrix of the prediction - at all x at `theta[k]`. - - `predinfo['covhalf']` : if `A = predinfo['covhalf'][k]` then - `A.T @ A = predinfo['cov'][k]`. - - fitinfo : dict - An arbitary dictionary where you placed all your important fitting - information from the fit function above. - - x : array of objects - An matrix (vector) of inputs for prediction. - - theta : array of objects - An matrix (vector) of parameters to prediction. - - kwargs : dict - A dictionary containing additional options - """ - return_grad = False - if ( - (kwargs is not None) - and ("return_grad" in kwargs.keys()) - and (kwargs["return_grad"] is True) - ): - return_grad = True - return_covx = True - if ( - (kwargs is not None) - and ("return_covx" in kwargs.keys()) - and (kwargs["return_covx"] is False) - ): - return_covx = False - infos = fitinfo["emulist"] - predvecs = np.zeros((theta.shape[0], len(infos))) - predvars = np.zeros((theta.shape[0], len(infos))) - predcovs = np.zeros((theta.shape[0], theta.shape[0], len(infos))) - - if predvecs.ndim < 1.5: - predvecs = predvecs.reshape((1, -1)) - predvars = predvars.reshape((1, -1)) - try: - if ( - x is None - or np.all(np.equal(x, fitinfo["x"])) - or np.allclose(x, fitinfo["x"]) - ): - xind = np.arange(0, x.shape[0]) - xnewind = np.arange(0, x.shape[0]) - else: - raise - except Exception: - matchingmatrix = np.ones((x.shape[0], fitinfo["x"].shape[0])) - for k in range(0, x[0].shape[0]): - try: - matchingmatrix *= np.isclose(x[:, k][:, None], fitinfo["x"][:, k]) - except Exception: - matchingmatrix *= np.equal(x[:, k][:, None], fitinfo["x"][:, k]) - xind = np.argwhere(matchingmatrix > 0.5)[:, 1] - xnewind = np.argwhere(matchingmatrix > 0.5)[:, 0] - - rsave = np.array(np.ones(len(infos)), dtype=object) - rsave_3 = np.array(np.ones(len(infos)), dtype=object) - # loop over principal components - for k in range(0, len(infos)): - if infos[k]["hypind"] == k: - # covariance matrix between new theta and thetas from fit. - rsave[k] = __covmat(theta, fitinfo["theta"], infos[k]["hypcov"]) - - rsave_3[k] = __covmat(theta, theta, infos[k]["hypcov"]) - # adjusted covariance matrix - r = (1 - infos[k]["nug"]) * np.squeeze(rsave[infos[k]["hypind"]]) - r3 = (1 - infos[k]["nug"]) * np.squeeze(rsave_3[infos[k]["hypind"]]) - try: - rVh = r @ infos[k]["Vh"] - rVh2 = rVh @ (infos[k]["Vh"]).T - except Exception: - for i in range(0, len(infos)): - print((i, infos[i]["hypind"])) - raise ValueError("Something went wrong with fitted components") - - if rVh.ndim < 1.5: - rVh = rVh.reshape((1, -1)) - if rVh2.ndim < 1.5: - rVh2 = np.reshape(rVh2, (1, -1)) - predvecs[:, k] = r @ infos[k]["pw"] - predvars[:, k] = infos[k]["sig2"] * np.abs(1 - np.sum(rVh**2, 1)) - predcovs[:, :, k] = infos[k]["sig2"] * ( - r3 - rVh @ rVh.T - ) # np.abs(1 - np.sum(rVh ** 2, 1)) - - # calculate predictive mean and variance - predinfo["mean"] = np.full((x.shape[0], theta.shape[0]), np.nan) - predinfo["var"] = np.full((x.shape[0], theta.shape[0]), np.nan) - predinfo["covx"] = np.full((theta.shape[0], theta.shape[0]), np.nan) - pctscale = (fitinfo["pcti"].T * fitinfo["standardpcinfo"]["scale"]).T - # pctscale = (fitinfo['pct'].T * fitinfo['standardpcinfo']['scale']).T - predinfo["mean"][xnewind, :] = ( - (predvecs @ pctscale[xind, :].T) + fitinfo["standardpcinfo"]["offset"][xind] - ).T - predinfo["var"][xnewind, :] = ( - ( - fitinfo["standardpcinfo"]["extravar"][xind] - + predvars @ (pctscale[xind, :] ** 2).T - ) - ).T - - predinfo["covx"] = predcovs[:, :, 0] * (pctscale[:, :] ** 2) - - # print(predinfo['var']) - # print(predinfo['cov']) - - predinfo["extravar"] = 1 * fitinfo["standardpcinfo"]["extravar"][xind] - predinfo["predvars"] = 1 * predvars - predinfo["predvecs"] = 1 * predvecs - predinfo["phi"] = 1 * pctscale[xind, :] - - return - - -def predictlpdf(predinfo, f, return_grad=False, addvar=0, **kwargs): - totvar = addvar + predinfo["extravar"] - rf = ((f.T - predinfo["mean"].T) * (1 / np.sqrt(totvar))).T - Gf = predinfo["phi"].T * (1 / np.sqrt(totvar)) - Gfrf = Gf @ rf - Gf2 = Gf @ Gf.T - likv = np.sum(rf**2, 0) - if return_grad: - rf2 = -( - predinfo["mean_gradtheta"].transpose(2, 1, 0) * (1 / np.sqrt(totvar)) - ).transpose(2, 1, 0) - Gfrf2 = (Gf @ rf2.transpose(1, 0, 2)).transpose(1, 0, 2) - dlikv = 2 * np.sum(rf2.transpose(2, 1, 0) * rf.transpose(1, 0), 2).T - for c in range(0, predinfo["predvars"].shape[0]): - w, v = np.linalg.eig(np.diag(1 / (predinfo["predvars"][c, :])) + Gf2) - term1 = (v * (1 / w)) @ (v.T @ Gfrf[:, c]) - - likv[c] -= Gfrf[:, c].T @ term1 - likv[c] += np.sum(np.log(predinfo["predvars"][c, :])) - likv[c] += np.sum(np.log(w)) - if return_grad: - Si = (v * (1 / w)) @ v.T - grt = ( - predinfo["predvars_gradtheta"][c, :, :].T / predinfo["predvars"][c, :] - ).T - dlikv[c, :] += np.sum(grt, 0) - grt = (-grt.T / predinfo["predvars"][c, :]).T - dlikv[c, :] += np.diag(Si) @ grt - term2 = (term1 / predinfo["predvars"][c, :]) ** 2 - dlikv[c, :] -= 2 * Gfrf2[:, c, :].T @ term1 - dlikv[c, :] -= term2 @ (predinfo["predvars_gradtheta"][c, :, :]) - if return_grad: - return (-likv / 2).reshape(-1, 1), (-dlikv / 2) - else: - return (-likv / 2).reshape(-1, 1) - - -def __standardizef(fitinfo, offset=None, scale=None): - r"""Standardizes f by creating offset, scale and fs.""" - # Extracting from input dictionary - f = fitinfo["f"] - epsilonPC = fitinfo["epsilonPC"] - - if (offset is not None) and (scale is not None): - if offset.shape[0] == f.shape[1] and scale.shape[0] == f.shape[1]: - if np.any(np.nanmean(np.abs(f - offset) / scale, 1) > 4): - offset = None - scale = None - else: - offset = None - scale = None - if offset is None or scale is None: - offset = np.zeros(f.shape[1]) - scale = np.zeros(f.shape[1]) - for k in range(0, f.shape[1]): - offset[k] = np.nanmean(f[:, k]) - scale[k] = np.nanstd(f[:, k]) / np.sqrt(1 - np.isnan(f[:, k]).mean()) - if scale[k] == 0: - print(f) - raise ValueError("You have a row that is non-varying.") - - fs = np.zeros(f.shape) - fs = (f - offset) / scale - - # Assigning new values to the dictionary - U, S, _ = np.linalg.svd(fs.T, full_matrices=False) - Sp = S**2 - epsilonPC - Up = U[:, Sp > 0] - - extravar = np.nanmean((fs - fs @ Up @ Up.T) ** 2, 0) * (scale**2) - - standardpcinfo = { - "offset": offset, - "scale": scale, - "fs": fs, - "U": U, - "S": S, - "extravar": extravar, - } - - fitinfo["standardpcinfo"] = standardpcinfo - return - - -def __PCs(fitinfo): - "Apply PCA to reduce the dimension of `f`." - # Extracting from input dictionary - f = fitinfo["f"] - epsilonPC = fitinfo["epsilonPC"] - fs = fitinfo["standardpcinfo"]["fs"] - - if "U" in fitinfo["standardpcinfo"]: - U = fitinfo["standardpcinfo"]["U"] - S = fitinfo["standardpcinfo"]["S"] - else: - U, S, _ = np.linalg.svd(fs.T, full_matrices=False) - Sp = S**2 - epsilonPC - pct = U[:, Sp > 0] - pcw = np.sqrt(Sp[Sp > 0]) - - pcstdvar = np.zeros((f.shape[0], pct.shape[1])) - fitinfo["pcw"] = pcw - fitinfo["pcto"] = 1 * pct - effn = np.sum(np.clip(1 - pcstdvar, 0, 1)) - fitinfo["pct"] = pct * pcw / np.sqrt(effn) - fitinfo["pcti"] = pct * (np.sqrt(effn) / pcw) - # fitinfo['pc'] = pc * (np.sqrt(effn) / pcw) - fitinfo["pc"] = fs @ fitinfo["pct"] - fitinfo["unscaled_pcstdvar"] = pcstdvar - return - - -def __fitGPs(fitinfo, theta, numpcs, hyp1, hyp2, varconstant): - """Fit emulators for all principle components.""" - if "emulist" in fitinfo.keys(): - hypstarts = np.zeros((numpcs, fitinfo["emulist"][0]["hyp"].shape[0])) - hypinds = -1 * np.ones(numpcs) - for pcanum in range(0, min(numpcs, len(fitinfo["emulist"]))): - hypstarts[pcanum, :] = fitinfo["emulist"][pcanum]["hyp"] - hypinds[pcanum] = fitinfo["emulist"][pcanum]["hypind"] - else: - hypstarts = None - hypinds = -1 * np.ones(numpcs) - - emulist = [dict() for x in range(0, numpcs)] - for iters in range(0, 3): - for pcanum in range(0, numpcs): - if np.sum(hypinds == np.array(range(0, numpcs))) > 0.5: - hypwhere = np.where(hypinds == np.array(range(0, numpcs)))[0] - emulist[pcanum] = __fitGP1d( - theta=theta, - g=fitinfo["pc"][:, pcanum], - hyp1=hyp1, - hyp2=hyp2, - hypvarconst=varconstant, - gvar=fitinfo["unscaled_pcstdvar"][:, pcanum], - dampalpha=fitinfo["dampalpha"], - eta=fitinfo["eta"], - hypstarts=hypstarts[hypwhere, :], - hypinds=hypwhere, - sig2ofconst=0.01, - ) - else: - emulist[pcanum] = __fitGP1d( - theta=theta, - g=fitinfo["pc"][:, pcanum], - hyp1=hyp1, - hyp2=hyp2, - hypvarconst=varconstant, - gvar=fitinfo["unscaled_pcstdvar"][:, pcanum], - dampalpha=fitinfo["dampalpha"], - eta=fitinfo["eta"], - sig2ofconst=0.01, - ) - hypstarts = np.zeros((numpcs, emulist[pcanum]["hyp"].shape[0])) - emulist[pcanum]["hypind"] = min(pcanum, emulist[pcanum]["hypind"]) - hypstarts[pcanum, :] = emulist[pcanum]["hyp"] - if emulist[pcanum]["hypind"] < -0.5: - emulist[pcanum]["hypind"] = 1 * pcanum - hypinds[pcanum] = 1 * emulist[pcanum]["hypind"] - return emulist - - -def __fitGP1d( - theta, - g, - hyp1, - hyp2, - hypvarconst, - gvar=None, - dampalpha=None, - eta=None, - hypstarts=None, - hypinds=None, - sig2ofconst=None, -): - """Return a fitted model from the emulator model using smart method.""" - hypvarconstmean = 4 if hypvarconst is None else hypvarconst - hypvarconstLB = -8 if hypvarconst is None else hypvarconst - 0.5 - hypvarconstUB = 8 if hypvarconst is None else hypvarconst + 0.5 - - subinfo = {} - subinfo["hypregmean"] = np.append( - 0 + 0.5 * np.log(theta.shape[1]) + np.log(np.std(theta, 0)), - (0, hypvarconstmean, hyp1), - ) - subinfo["hypregLB"] = np.append( - -4 + 0.5 * np.log(theta.shape[1]) + np.log(np.std(theta, 0)), - (-12, hypvarconstLB, hyp2), - ) - - subinfo["hypregUB"] = np.append( - 4 + 0.5 * np.log(theta.shape[1]) + np.log(np.std(theta, 0)), - (2, hypvarconstUB, -8), - ) - subinfo["hypregstd"] = (subinfo["hypregUB"] - subinfo["hypregLB"]) / 8 - subinfo["hypregstd"][-3] = 2 - subinfo["hypregstd"][-1] = 4 - subinfo["hyp"] = 1 * subinfo["hypregmean"] - nhyptrain = np.max(np.min((20 * theta.shape[1], theta.shape[0]))) - if theta.shape[0] > nhyptrain: - thetac = np.random.choice(theta.shape[0], nhyptrain, replace=False) - else: - thetac = range(0, theta.shape[0]) - subinfo["theta"] = theta[thetac, :] - subinfo["g"] = g[thetac] - - # maxgvar = np.max(gvar) - # gvar = gvar / ((np.abs(maxgvar*1.001 - gvar)) ** dampalpha) - - gvar = np.minimum(eta, gvar / ((1 - gvar) ** dampalpha)) - - # print(gvar) - subinfo["sig2ofconst"] = sig2ofconst - subinfo["gvar"] = gvar[thetac] - hypind0 = -1 - - L0 = __negloglik(subinfo["hyp"], subinfo) - if hypstarts is not None: - L0 = __negloglik(subinfo["hyp"], subinfo) - for k in range(0, hypstarts.shape[0]): - L1 = __negloglik(hypstarts[k, :], subinfo) - if L1 < L0: - subinfo["hyp"] = hypstarts[k, :] - L0 = 1 * L1 - hypind0 = hypinds[k] - - if hypind0 > -0.5 and hypstarts.ndim > 1: - dL = __negloglikgrad(subinfo["hyp"], subinfo) - scalL = np.std(hypstarts, 0) * hypstarts.shape[0] / (1 + hypstarts.shape[0]) + ( - 1 / (1 + hypstarts.shape[0]) * subinfo["hypregstd"] - ) - if np.sum((dL * scalL) ** 2) < 1.25 * ( - subinfo["hyp"].shape[0] + 5 * np.sqrt(subinfo["hyp"].shape[0]) - ): - skipop = True - else: - skipop = False - else: - skipop = False - - if not skipop: - - def scaledlik(hypv): - hyprs = subinfo["hypregmean"] + hypv * subinfo["hypregstd"] - return __negloglik(hyprs, subinfo) - - def scaledlikgrad(hypv): - hyprs = subinfo["hypregmean"] + hypv * subinfo["hypregstd"] - return __negloglikgrad(hyprs, subinfo) * subinfo["hypregstd"] - - newLB = (subinfo["hypregLB"] - subinfo["hypregmean"]) / subinfo["hypregstd"] - newUB = (subinfo["hypregUB"] - subinfo["hypregmean"]) / subinfo["hypregstd"] - - newhyp0 = (subinfo["hyp"] - subinfo["hypregmean"]) / subinfo["hypregstd"] - - opval = spo.minimize( - scaledlik, - newhyp0, - method="L-BFGS-B", - options={"gtol": 0.1}, - jac=scaledlikgrad, - bounds=spo.Bounds(newLB, newUB), - ) - - hypn = subinfo["hypregmean"] + opval.x * subinfo["hypregstd"] - likdiff = L0 - __negloglik(hypn, subinfo) - else: - likdiff = 0 - if hypind0 > -0.5 and (2 * likdiff) < 1.25 * ( - subinfo["hyp"].shape[0] + 5 * np.sqrt(subinfo["hyp"].shape[0]) - ): - subinfo["hypcov"] = subinfo["hyp"][:-2] - subinfo["hypvarconst"] = subinfo["hyp"][-2] - subinfo["hypind"] = hypind0 - subinfo["nug"] = np.exp(subinfo["hyp"][-1]) / (1 + np.exp(subinfo["hyp"][-1])) - - R = __covmat(theta, theta, subinfo["hypcov"]) - - subinfo["R"] = (1 - subinfo["nug"]) * R + subinfo["nug"] * np.eye(R.shape[0]) - if gvar is not None: - subinfo["R"] += np.exp(subinfo["hypvarconst"]) * np.diag(gvar) - - W, V = np.linalg.eigh(subinfo["R"]) - Vh = V / np.sqrt(np.abs(W)) - fcenter = Vh.T @ g - subinfo["Vh"] = Vh - n = subinfo["R"].shape[0] - subinfo["sig2"] = (np.mean(fcenter**2) * n + sig2ofconst) / (n + sig2ofconst) - subinfo["Rinv"] = V @ np.diag(1 / W) @ V.T - else: - subinfo["hyp"] = hypn - subinfo["hypind"] = -1 - subinfo["hypcov"] = subinfo["hyp"][:-2] - subinfo["hypvarconst"] = subinfo["hyp"][-2] - subinfo["nug"] = np.exp(subinfo["hyp"][-1]) / (1 + np.exp(subinfo["hyp"][-1])) - - R = __covmat(theta, theta, subinfo["hypcov"]) - subinfo["R"] = (1 - subinfo["nug"]) * R + subinfo["nug"] * np.eye(R.shape[0]) - if gvar is not None: - subinfo["R"] += np.exp(subinfo["hypvarconst"]) * np.diag(gvar) - n = subinfo["R"].shape[0] - W, V = np.linalg.eigh(subinfo["R"]) - Vh = V / np.sqrt(np.abs(W)) - fcenter = Vh.T @ g - subinfo["sig2"] = (np.mean(fcenter**2) * n + sig2ofconst) / (n + sig2ofconst) - subinfo["Rinv"] = Vh @ Vh.T - subinfo["Vh"] = Vh - subinfo["pw"] = subinfo["Rinv"] @ g - return subinfo - - -def __negloglik(hyp, info): - """Return penalized log likelihood of single demensional GP model.""" - R0 = __covmat(info["theta"], info["theta"], hyp[:-2]) - nug = np.exp(hyp[-1]) / (1 + np.exp(hyp[-1])) - R = (1 - nug) * R0 + nug * np.eye(info["theta"].shape[0]) - - if info["gvar"] is not None: - R += np.exp(hyp[-2]) * np.diag(info["gvar"]) - W, V = np.linalg.eigh(R) - Vh = V / np.sqrt(np.abs(W)) - fcenter = Vh.T @ info["g"] - n = info["g"].shape[0] - - sig2ofconst = info["sig2ofconst"] - sig2hat = (n * np.mean(fcenter**2) + sig2ofconst) / (n + sig2ofconst) - negloglik = 1 / 2 * np.sum(np.log(np.abs(W))) + 1 / 2 * n * np.log(sig2hat) - negloglik += 0.5 * np.sum( - ((10 ** (-8) + hyp - info["hypregmean"]) / (info["hypregstd"])) ** 2 - ) - return negloglik - - -def __negloglikgrad(hyp, info): - """Return gradient of the penalized log likelihood of single demensional - GP model.""" - R0, dR = __covmat(info["theta"], info["theta"], hyp[:-2], True) - nug = np.exp(hyp[-1]) / (1 + np.exp(hyp[-1])) - R = (1 - nug) * R0 + nug * np.eye(info["theta"].shape[0]) - dR = (1 - nug) * dR - dRappend2 = nug / (1 + np.exp(hyp[-1])) * (-R0 + np.eye(info["theta"].shape[0])) - - if info["gvar"] is not None: - R += np.exp(hyp[-2]) * np.diag(info["gvar"]) - dRappend1 = np.exp(hyp[-2]) * np.diag(info["gvar"]) - else: - dRappend1 = 0 * np.eye(info["theta"].shape[0]) - - dR = np.append(dR, dRappend1[:, :, None], axis=2) - dR = np.append(dR, dRappend2[:, :, None], axis=2) - W, V = np.linalg.eigh(R) - Vh = V / np.sqrt(np.abs(W)) - fcenter = Vh.T @ info["g"] - n = info["g"].shape[0] - - sig2ofconst = info["sig2ofconst"] - sig2hat = (n * np.mean(fcenter**2) + sig2ofconst) / (n + sig2ofconst) - dnegloglik = np.zeros(dR.shape[2]) - Rinv = Vh @ Vh.T - - for k in range(0, dR.shape[2]): - dsig2hat = -np.sum( - (Vh @ np.multiply.outer(fcenter, fcenter) @ Vh.T) * dR[:, :, k] - ) / (n + sig2ofconst) - dnegloglik[k] += 0.5 * n * dsig2hat / sig2hat - dnegloglik[k] += 0.5 * np.sum(Rinv * dR[:, :, k]) - - dnegloglik += (10 ** (-8) + hyp - info["hypregmean"]) / ((info["hypregstd"]) ** 2) - return dnegloglik - - -def postphimat( - fitinfo, n_x, theta, obs, obsvar, theta_cand, covmat_ref, rVh_1_3D, pred_mean -): - - # n_x = len(x) - n_tot_ref = theta.shape[0] - n_ref = int(n_tot_ref / n_x) - n_t = fitinfo["theta"].shape[0] - - infos = fitinfo["emulist"] - predvars_cand = np.zeros((theta_cand.shape[0], len(infos))) - - # n_ref x n_cand - rsave_2 = np.array(np.ones(len(infos)), dtype=object) - # n_cand x n_t - rsave_4 = np.array(np.ones(len(infos)), dtype=object) - - # loop over principal components - for k in range(0, len(infos)): - - if infos[k]["hypind"] == k: - rsave_2[k] = __covmat(theta, theta_cand, infos[k]["hypcov"]) - - rsave_4[k] = __covmat(theta_cand, fitinfo["theta"], infos[k]["hypcov"]) - - # adjusted covariance matrix - # n_tot_ref - r_2 = (1 - infos[k]["nug"]) * np.squeeze(rsave_2[infos[k]["hypind"]]) - # n_acquired - r_4 = (1 - infos[k]["nug"]) * np.squeeze(rsave_4[infos[k]["hypind"]]) - - try: - rVh_4 = r_4.reshape(1, len(fitinfo["theta"])) @ infos[k]["Vh"] - - except Exception: - for i in range(0, len(infos)): - print((i, infos[i]["hypind"])) - raise ValueError("Something went wrong with fitted components") - - # print(np.round(r_2[0:50], 4)) - r_2_3D = r_2.reshape(n_ref, n_x, 1) - # print(np.round(r_2_3D[0:5, 0:n_x, 0], 4)) - rVh_4_3D = rVh_4.reshape(1, n_t, 1) - - # rVh_1_3D: n_ref x n_x x n_acquired - - # n_ref x n_x x 1 - cov3D = np.matmul(rVh_1_3D, rVh_4_3D) - - # n_ref x n_x x 1 - cov_cand_3D = infos[k]["sig2"] * (r_2_3D - cov3D) - - predvars_cand[:, k] = infos[k]["sig2"] * np.abs(1 - np.sum(rVh_4**2, 1)) - predvars_cand[:, k] += infos[k]["nug"] - - # rVh_4: used to compute candidate variance - - # calculate candidate variance - pctscale = (fitinfo["pcti"].T * fitinfo["standardpcinfo"]["scale"]).T - - # n_ref x n_x x 1 - cov_cand_3D = cov_cand_3D * (pctscale[:, :] ** 2) - # n_ref x 1 x n_x - cov_cand_3DT = np.transpose(cov_cand_3D, (0, 2, 1)) - - # (1 x 1) - var_cand = ( - ( - fitinfo["standardpcinfo"]["extravar"][:] - + predvars_cand @ (pctscale[:, :] ** 2).T - ) - ).T - - # (n_ref x n_x x n_x) - Phi3D = (cov_cand_3D * cov_cand_3DT) / var_cand - cov1 = 0.5 * (covmat_ref + Phi3D) - cov2 = covmat_ref - Phi3D - p1 = multiple_pdfs(obs, pred_mean, cov1) - - det1 = multiple_determinants(cov2) - eivar = np.sum((1 / ((2**n_x) * (np.sqrt(np.pi) ** n_x) * np.sqrt(det1))) * p1) - return eivar - - -def temp_postphimat(fitinfo, n_x, theta, obs, obsvar): - - n_tot_ref = theta.shape[0] - n_ref = int(n_tot_ref / n_x) - n_t = fitinfo["theta"].shape[0] - - infos = fitinfo["emulist"] - predmean_ref = np.zeros((theta.shape[0], len(infos))) - predvars_ref = np.zeros((theta.shape[0], len(infos))) - - if predmean_ref.ndim < 1.5: - predmean_ref = predmean_ref.reshape((1, -1)) - predvars_ref = predvars_ref.reshape((1, -1)) - - # n_ref x n_t - rsave_1 = np.array(np.ones(len(infos)), dtype=object) - # n_ref x n_ref - rsave_3 = np.array(np.ones(len(infos)), dtype=object) - - # loop over principal components - for k in range(0, len(infos)): - if infos[k]["hypind"] == k: - # covariance matrix between new theta and thetas from fit. - rsave_1[k] = __covmat(theta, fitinfo["theta"], infos[k]["hypcov"]) - - rsave_3[k] = __covmat(theta, theta, infos[k]["hypcov"]) - - # adjusted covariance matrix - r_1 = (1 - infos[k]["nug"]) * np.squeeze(rsave_1[infos[k]["hypind"]]) - r_3 = (1 - infos[k]["nug"]) * np.squeeze(rsave_3[infos[k]["hypind"]]) - - try: - rVh_1 = r_1 @ infos[k]["Vh"] - - except Exception: - for i in range(0, len(infos)): - print((i, infos[i]["hypind"])) - raise ValueError("Something went wrong with fitted components") - - if rVh_1.ndim < 1.5: - rVh_1 = rVh_1.reshape((1, -1)) - - id_row = np.arange(0, n_tot_ref) - id_col = np.arange(0, n_tot_ref).reshape(n_ref, n_x) - id_col = np.repeat(id_col, repeats=n_x, axis=0) - - # r_3 : n_tot_ref x n_tot_ref - # r_3_3D : n_ref x n_x x n_x - r_3_3D = r_3[id_row[:, None], id_col].reshape(n_ref, n_x, n_x) - - # rVh_1 : n_tot_ref x n_t - # rVh_1_3d : n_ref x n_x x n_t - rVh_1_3d = rVh_1.reshape(n_ref, n_x, n_t) - # rVh_1_3dT : n_ref x n_t x n_x - rVh_1_3dT = np.transpose(rVh_1_3d, (0, 2, 1)) - cov3D = np.matmul(rVh_1_3d, rVh_1_3dT) - - # cov3D : n_ref x n_x x n_x - cov_ref_3D = infos[k]["sig2"] * (r_3_3D - cov3D) - predmean_ref[:, k] = r_1 @ infos[k]["pw"] - - # print(np.round(obsvar, 2)) - # calculate predictive mean and variance - pctscale = (fitinfo["pcti"].T * fitinfo["standardpcinfo"]["scale"]).T - Smat3D = cov_ref_3D * (pctscale[:, :] ** 2) - pred_mean = ((predmean_ref @ pctscale.T) + fitinfo["standardpcinfo"]["offset"]).T - pred_mean = pred_mean.reshape(n_ref, n_x) - covmat_ref = Smat3D + obsvar - return covmat_ref, rVh_1_3d, pred_mean - - -def postpred(fitinfo, x, theta, obs, obsvar): - - n_x = len(x) - n_tot_ref = theta.shape[0] - n_ref = int(n_tot_ref / n_x) - n_t = fitinfo["theta"].shape[0] - - predinfo = {} - infos = fitinfo["emulist"] - predmean_ref = np.zeros((theta.shape[0], len(infos))) - predvars_ref = np.zeros((theta.shape[0], len(infos))) - - if predmean_ref.ndim < 1.5: - predmean_ref = predmean_ref.reshape((1, -1)) - predvars_ref = predvars_ref.reshape((1, -1)) - - # n_ref x n_t - rsave_1 = np.array(np.ones(len(infos)), dtype=object) - # n_ref x n_ref - rsave_3 = np.array(np.ones(len(infos)), dtype=object) - - # loop over principal components - for k in range(0, len(infos)): - if infos[k]["hypind"] == k: - # covariance matrix between new theta and thetas from fit. - rsave_1[k] = __covmat(theta, fitinfo["theta"], infos[k]["hypcov"]) - - rsave_3[k] = __covmat(theta, theta, infos[k]["hypcov"]) - - # adjusted covariance matrix - r_1 = (1 - infos[k]["nug"]) * np.squeeze(rsave_1[infos[k]["hypind"]]) - r_3 = (1 - infos[k]["nug"]) * np.squeeze(rsave_3[infos[k]["hypind"]]) - - try: - rVh_1 = r_1 @ infos[k]["Vh"] - - except Exception: - for i in range(0, len(infos)): - print((i, infos[i]["hypind"])) - raise ValueError("Something went wrong with fitted components") - - if rVh_1.ndim < 1.5: - rVh_1 = rVh_1.reshape((1, -1)) - - id_row = np.arange(0, n_tot_ref) - id_col = np.arange(0, n_tot_ref).reshape(n_ref, n_x) - id_col = np.repeat(id_col, repeats=n_x, axis=0) - - r_3_3D = r_3[id_row[:, None], id_col].reshape(n_ref, n_x, n_x) - - rVh_1_3d = rVh_1.reshape(n_ref, n_x, n_t) - rVh_1_3dT = np.transpose(rVh_1_3d, (0, 2, 1)) - cov3D = np.matmul(rVh_1_3d, rVh_1_3dT) - cov_ref_3D = infos[k]["sig2"] * (r_3_3D - cov3D) - - predmean_ref[:, k] = r_1 @ infos[k]["pw"] - - # calculate predictive mean and variance - predinfo["mean"] = np.full((x.shape[0], int(theta.shape[0] / x.shape[0])), np.nan) - - pctscale = (fitinfo["pcti"].T * fitinfo["standardpcinfo"]["scale"]).T - Smat3D = cov_ref_3D * (pctscale[:, :] ** 2) - - predinfo["mean"] = ( - (predmean_ref @ pctscale.T) + fitinfo["standardpcinfo"]["offset"] - ).T - predinfo["mean"] = predinfo["mean"].reshape(n_ref, n_x) - - d = x.shape[0] - - obsvar3D = obsvar.reshape(1, n_x, n_x) - cov1 = 0.5 * obsvar3D + Smat3D - cov2 = Smat3D + obsvar3D - # cov2 = obsvar3D - p1 = multiple_pdfs(obs, predinfo["mean"], cov1) - postmean = multiple_pdfs(obs, predinfo["mean"], cov2) - - det1 = multiple_determinants(obsvar3D) - postvar = (1 / ((2**d) * (np.sqrt(np.pi) ** d) * np.sqrt(det1))) * p1 - postmean**2 - - return postmean, postvar - - -def postpredbias(fitinfo, x, theta, obs, obsvar, biasmean): - - n_x = len(x) - n_tot_ref = theta.shape[0] - n_ref = int(n_tot_ref / n_x) - n_t = fitinfo["theta"].shape[0] - - predinfo = {} - infos = fitinfo["emulist"] - predmean_ref = np.zeros((theta.shape[0], len(infos))) - predvars_ref = np.zeros((theta.shape[0], len(infos))) - - if predmean_ref.ndim < 1.5: - predmean_ref = predmean_ref.reshape((1, -1)) - predvars_ref = predvars_ref.reshape((1, -1)) - - # n_ref x n_t - rsave_1 = np.array(np.ones(len(infos)), dtype=object) - # n_ref x n_ref - rsave_3 = np.array(np.ones(len(infos)), dtype=object) - - # loop over principal components - for k in range(0, len(infos)): - if infos[k]["hypind"] == k: - # covariance matrix between new theta and thetas from fit. - rsave_1[k] = __covmat(theta, fitinfo["theta"], infos[k]["hypcov"]) - - rsave_3[k] = __covmat(theta, theta, infos[k]["hypcov"]) - - # adjusted covariance matrix - r_1 = (1 - infos[k]["nug"]) * np.squeeze(rsave_1[infos[k]["hypind"]]) - r_3 = (1 - infos[k]["nug"]) * np.squeeze(rsave_3[infos[k]["hypind"]]) - - try: - rVh_1 = r_1 @ infos[k]["Vh"] - - except Exception: - for i in range(0, len(infos)): - print((i, infos[i]["hypind"])) - raise ValueError("Something went wrong with fitted components") - - if rVh_1.ndim < 1.5: - rVh_1 = rVh_1.reshape((1, -1)) - - id_row = np.arange(0, n_tot_ref) - id_col = np.arange(0, n_tot_ref).reshape(n_ref, n_x) - id_col = np.repeat(id_col, repeats=n_x, axis=0) - # print(r_3) - # r_3 = np.array(r_3).reshape(1,1) - r_3_3D = r_3[id_row[:, None], id_col].reshape(n_ref, n_x, n_x) - - rVh_1_3d = rVh_1.reshape(n_ref, n_x, n_t) - rVh_1_3dT = np.transpose(rVh_1_3d, (0, 2, 1)) - cov3D = np.matmul(rVh_1_3d, rVh_1_3dT) - cov_ref_3D = infos[k]["sig2"] * (r_3_3D - cov3D) - - predmean_ref[:, k] = r_1 @ infos[k]["pw"] - - # calculate predictive mean and variance - predinfo["mean"] = np.full((x.shape[0], int(theta.shape[0] / x.shape[0])), np.nan) - - pctscale = (fitinfo["pcti"].T * fitinfo["standardpcinfo"]["scale"]).T - Smat3D = cov_ref_3D * (pctscale[:, :] ** 2) - - predinfo["mean"] = ( - (predmean_ref @ pctscale.T) + fitinfo["standardpcinfo"]["offset"] - ).T - predinfo["mean"] = predinfo["mean"].reshape(n_ref, n_x) - - d = x.shape[0] - - obsvar3D = obsvar.reshape(1, n_x, n_x) - cov1 = 0.5 * obsvar3D + Smat3D - cov2 = Smat3D + obsvar3D - # cov2 = obsvar3D - - p1 = multiple_pdfs(obs, predinfo["mean"] + biasmean, cov1) - postmean = multiple_pdfs(obs, predinfo["mean"] + biasmean, cov2) - - det1 = multiple_determinants(obsvar3D) - postvar = (1 / ((2**d) * (np.sqrt(np.pi) ** d) * np.sqrt(det1))) * p1 - postmean**2 - - return postmean, postvar - - -def imspe_acq(fitinfo, theta, theta_cand): - - infos = fitinfo["emulist"] - predvars_cand = np.zeros((theta_cand.shape[0], len(infos))) - - # n_ref x n_t - rsave_1 = np.array(np.ones(len(infos)), dtype=object) - # n_ref x n_cand - rsave_2 = np.array(np.ones(len(infos)), dtype=object) - # n_cand x n_t - rsave_4 = np.array(np.ones(len(infos)), dtype=object) - - # loop over principal components - for k in range(0, len(infos)): - - if infos[k]["hypind"] == k: - rsave_1[k] = __covmat(theta, fitinfo["theta"], infos[k]["hypcov"]) - - rsave_2[k] = __covmat(theta, theta_cand, infos[k]["hypcov"]) - - rsave_4[k] = __covmat(theta_cand, fitinfo["theta"], infos[k]["hypcov"]) - - # nref x ntrain - r_1 = (1 - infos[k]["nug"]) * np.squeeze(rsave_1[infos[k]["hypind"]]) - # nref - r_2 = (1 - infos[k]["nug"]) * np.squeeze(rsave_2[infos[k]["hypind"]]) - # ntrain - r_4 = (1 - infos[k]["nug"]) * np.squeeze(rsave_4[infos[k]["hypind"]]) - - try: - # nref x 1 - r_2 = r_2[:, None] - # 1 x ntrain - rVh_4 = r_4.reshape(1, len(fitinfo["theta"])) @ infos[k]["Vh"] - # nref x ntrain - rVh_1 = r_1 @ infos[k]["Vh"] - - except Exception: - for i in range(0, len(infos)): - print((i, infos[i]["hypind"])) - raise ValueError("Something went wrong with fitted components") - - # nref x 1 - cov3D = rVh_1 @ rVh_4.T - cov3D = infos[k]["sig2"] * (r_2 - cov3D) - predvars_cand[:, k] = infos[k]["sig2"] * np.abs(1 - np.sum(rVh_4**2, 1)) - predvars_cand[:, k] += infos[k]["nug"] - - # calculate candidate variance - pctscale = (fitinfo["pcti"].T * fitinfo["standardpcinfo"]["scale"]).T - cov3D = cov3D * (pctscale[:, :] ** 2) - var_cand = ( - ( - fitinfo["standardpcinfo"]["extravar"][:] - + predvars_cand @ (pctscale[:, :] ** 2).T - ) - ).T - - Phi3D = (cov3D * cov3D) / var_cand - return np.sum(Phi3D) diff --git a/PUQ/surrogatemethods/hetGP.py b/PUQ/surrogatemethods/hetGP.py new file mode 100644 index 0000000..ad1ef0d --- /dev/null +++ b/PUQ/surrogatemethods/hetGP.py @@ -0,0 +1,198 @@ +import numpy as np +from hetgpy import hetGP, homGP +from PUQ.surrogatemethods.homGP import GPWrapper + +############################################################################### +## Heterogeneous GP with all options for the fit +############################################################################### + +## ' log-likelihood in the anisotropic case - one lengthscale by variable +## ' Model: K = nu2 * (C + Lambda) = nu using all observations using the replicates information +## ' nu2 is replaced by its plugin estimator in the likelihood +## ' @param X0 unique designs +## ' @param Z0 averaged observations +## ' @param Z replicated observations (sorted with respect to X0) +## ' @param mult number of replicates at each Xi +## ' @param Delta vector of nuggets corresponding to each X0i or pXi, that are smoothed to give Lambda +## ' @param logN should exponentiated variance be used +## ' @param SiNK should the smoothing come from the SiNK predictor instead of the kriging one +## ' @param theta scale parameter for the mean process, either one value (isotropic) or a vector (anistropic) +## ' @param k_theta_g constant used for linking nuggets lengthscale to mean process lengthscale, i.e., theta_g[k] = k_theta_g * theta[k], alternatively theta_g can be used +## ' @param theta_g either one value (isotropic) or a vector (anistropic), alternative to using k_theta_g +## ' @param g nugget of the nugget process +## ' @param pX matrix of pseudo inputs locations of the noise process for Delta (could be replaced by a vector to avoid double loop) +## ' @param beta0 mean, if not provided, the MLE estimator is used +## ' @param eps minimal value of elements of Lambda +## ' @param covtype covariance kernel type +## ' @param penalty should a penalty term on Delta be used? +## ' @param hom_ll reference homoskedastic likelihood +## ' @export + + +def fit( + fitinfo, + x, + theta, + f, + lower=None, + upper=None, + maxit=100, + noiseControl={"k_theta_g_bounds": (1, 100), "g_max": 1e2, "g_bounds": (1e-6, 1)}, + init={}, + known={}, + eps=np.sqrt(np.finfo(float).eps), + settings={ + "linkThetas": "joint", + "logN": True, + "initStrategy": "residuals", + "checkHom": True, + "penalty": True, + "trace": 0, + "return.matrices": True, + "return.hom": False, + "factr": 1e9, + }, + covtype="Gaussian", + **kwargs, +): + r""" + Wrapper function for hetgpy.hetGP.mleHetGP + + Arguments + --------- + fitinfo: dictionary/class holding emulator results + x : ndarray_like + matrix of all designs, one per row, or list with elements: + - ``X0`` matrix of unique design locations, one point per row + - ``Z0`` vector of averaged observations, of length ``len(X0)`` + - ``mult`` number of replicates at designs in ``X0``, of length ``len(X0)`` + theta: not used + f : ndarray_like + Z vector of all observations. If using a list with ``X``, ``Z`` has to be ordered with respect to ``X0``, and of length ``sum(mult)`` + lower,upper : ndarray_like + optional bounds for the ``theta`` parameter (see :func: covariance_functions.cov_gen for the exact parameterization). + In the multivariate case, it is possible to give vectors for bounds (resp. scalars) for anisotropy (resp. isotropy) + noiseControl : dict + dict with elements related to optimization of the noise process parameters: + - ``g_min``, ``g_max`` minimal and maximal noise to signal ratio (of the mean process) + - ``lowerDelta``, ``upperDelta`` optional vectors (or scalars) of bounds on ``Delta``, of length ``len(X0)`` (default to ``np.repeat(eps, X0.shape[0])`` and ``np.repeat(noiseControl["g_max"], X0.shape[0])`` resp., or their ``log``) + - ``lowerpX``, ``upperpX`` optional vectors of bounds of the input domain if `pX` is used. + - ``lowerTheta_g``, ``upperTheta_g`` optional vectors of bounds for the lengthscales of the noise process if ``linkThetas == 'none'``. Same as for ``theta`` if not provided. + - ``k_theta_g_bounds`` if ``linkThetas == 'joint'``, vector with minimal and maximal values for ``k_theta_g`` (default to ``(1, 100)``). See Notes. + - ``g_bounds`` vector for minimal and maximal noise to signal ratios for the noise of the noise process, i.e., the smoothing parameter for the noise process. (default to ``(1e-6, 1)``). + settings : dict + dict for options about the general modeling procedure, with elements: + - ``linkThetas`` defines the relation between lengthscales of the mean and noise processes. Either ``'none'``, ``'joint'``(default) or ``'constr'``, see Notes. + - ``logN``, when ``True`` (default), the log-noise process is modeled. + - ``initStrategy`` one of ``'simple'``, ``'residuals'`` (default) and ``'smoothed'`` to obtain starting values for ``Delta``, see Notes + - ``penalty`` when ``True``, the penalized version of the likelihood is used (i.e., the sum of the log-likelihoods of the mean and variance processes, see References). + - ``hardpenalty`` is ``True``, the log-likelihood from the noise GP is taken into account only if negative (default if ``maxit > 1000``). + - ``checkHom`` when ``True``, if the log-likelihood with a homoskedastic model is better, then return it. + - ``trace`` optional scalar (default to ``0``). If negative, fit silently. If ``0``, only high level information is given. If ``1``, information is given about the result of the heterogeneous model optimization. Level ``2`` gives more details. Level ``3`` additionaly displays all details about initialization of hyperparameters. + - ``return_matrices`` boolean to include the inverse covariance matrix in the object for further use (e.g., prediction). + - ``return_hom`` boolean to include homoskedastic GP models used for initialization (i.e., ``modHom`` and ``modNugs``). + - ``factr`` (default to 1e9) and ``pgtol`` are available to be passed to `options` for L-BFGS-B in :func: ``scipy.optimize.minimize``. + eps : float + jitter used in the inversion of the covariance matrix for numerical stability + init,known : dict + optional lists of starting values for mle optimization or that should not be optimized over, respectively. + Values in ``known`` are not modified, while it can happen to these of ``init``, see Notes. + One can set one or several of the following: + - ``theta`` lengthscale parameter(s) for the mean process either one value (isotropic) or a vector (anistropic) + - ``Delta`` vector of nuggets corresponding to each design in ``X0``, that are smoothed to give ``Lambda`` (as the global covariance matrix depends on ``Delta`` and ``nu_hat``, it is recommended to also pass values for ``theta``) + - ``beta0`` constant trend of the mean process + - ``k_theta_g`` constant used for link mean and noise processes lengthscales, when ``settings['linkThetas'] == 'joint'`` + - ``theta_g`` either one value (isotropic) or a vector (anistropic) for lengthscale parameter(s) of the noise process, when ``settings['linkThetas'] != 'joint'`` + - ``g`` scalar nugget of the noise process + - ``g_H`` scalar homoskedastic nugget for the initialisation with a :func: homGP.mleHomGP. See Notes. + - ``pX`` matrix of fixed pseudo inputs locations of the noise process corresponding to Delta + covtype : str + covariance kernel type, either ``'Gaussian'``, ``'Matern5_2'`` or ``'Matern3_2'``, see :func: ``~covariance_functions.cov_gen`` + maxit : int + maximum number of iterations for `L-BFGS-B` of :func: ``scipy.optimize.minimize`` dedicated to maximum likelihood optimization + """ + + f = f.flatten() + model = hetGP() + model.mle( + X=x, + Z=f, + known=known, + noiseControl=noiseControl, + lower=lower, + upper=upper, + maxit=maxit, + settings=settings, + init=init, + eps=eps, + covtype=covtype, + ) + for key in model.__dict__.keys(): + fitinfo[key] = model.get(key) + fitinfo["is_homGP"] = isinstance(model, homGP) + return + + +def predict(predinfo, fitinfo, x, theta, thetaprime=None, rep_no=None, **kwargs): + r""" + Wrapper method for hetgpy.hetGP.predict + """ + GP = fitinfo.get("model") + if GP is None: + # use wrapper class to instantiate trained GP + GP = GPWrapper(fitinfo=fitinfo) + + # handle kws + kws = {} + eligible_keys = ["nugs_only", "interval", "interval_lower", "interval_upper"] + for key in eligible_keys: + if key in kwargs.keys(): + kws[key] = kwargs.get(key) + + preds = GP.predict(x=x, xprime=thetaprime, **kws) + # ensure naming consistency + predinfo["mean"] = preds.get("mean") + predinfo["var"] = preds.get("sd2") + predinfo["nugs"] = preds.get("nugs") + predinfo["covmat"] = preds.get("cov") + return + + +def update(fitinfo, x, Y=None, **kwargs): + r""" + Update function for hetGP + + Parameters + ---------- + fitinfo: dictionary that contains the fit information for a hetgpy.hetGP object + x: array of new design locations + Y: new response. If None, then + kwargs: key-value pairs that get passed to hetgpy.hetGP.update. + Must be one of: ginit, lower, upper, noiseControl, settings, known, maxit, method + """ + # validate kwargs + valid_kws = ( + "ginit", + "lower", + "upper", + "noiseControl", + "settings", + "known", + "maxit", + "method", + ) + for kw in kwargs.keys(): + if kw not in valid_kws: + raise ValueError(f"{kw} not found, must be one of {valid_kws}") + GP = GPWrapper(fitinfo) + if Y is None: + maxit = 0 # impute mean response and do not update hyperparams + Y = GP.predict(x)["mean"] + else: + maxit = kwargs.get("maxit", 100) + kwargs["maxit"] = maxit + GP.update(Xnew=x, Znew=Y, **kwargs) + for key in GP.__dict__.keys(): + fitinfo[key] = GP.get(key) + del GP + return diff --git a/PUQ/surrogatemethods/homGP.py b/PUQ/surrogatemethods/homGP.py new file mode 100644 index 0000000..89c02b6 --- /dev/null +++ b/PUQ/surrogatemethods/homGP.py @@ -0,0 +1,206 @@ +import numpy as np +from hetgpy import homGP, hetGP +from hetgpy.auto_bounds import auto_bounds +from hetgpy.find_reps import find_reps + +################################################################################ +## Homoskedastic noise +################################################################################ + +## Model: noisy observations with unknown homoskedastic noise +## K = nu^2 * (C + g * I) +# X0 unique designs matrix +# Z0 averaged observations at X0 +# Z observations vector (all observations) +# mult number of replicates at each unique design +# theta vector of lengthscale hyperparameters (or one for isotropy) +# g noise variance for the process +# beta0 trend + + +def fit( + fitinfo, + x, + theta, + f, + lower=None, + upper=None, + known={}, + noiseControl={"g_bounds": [np.sqrt(np.finfo(float).eps), 100]}, + init={}, + covtype="Gaussian", + maxit=100, + eps=np.sqrt(np.finfo(float).eps), + settings={"return.Ki": True, "factr": 1e7}, + **kwargs, +): + r""" + Wrapper function for hetgpy.homGP.mleHomGP + + Arguments + --------- + fitinfo: dictionary/class of results + x: nxd design matrix (must have at least one column) + f: output array for training + theta: not used + lower,upper : ndarray_like + optional bounds for the ``theta`` parameter (see :func: covariance_functions.cov_gen for the exact parameterization). + In the multivariate case, it is possible to give vectors for bounds (resp. scalars) for anisotropy (resp. isotropy) + noiseControl : dict + dict with element: + - ``g_bounds`` vector providing minimal and maximal noise to signal ratio (default to ``(sqrt(MACHINE_DOUBLE_EPS), 100)``). + settings : dict + dict for options about the general modeling procedure, with elements: + - ``return_Ki`` boolean to include the inverse covariance matrix in the object for further use (e.g., prediction). + - ``factr`` (default to 1e7) and ``pgtol`` are available to be passed to `options` for L-BFGS-B in :func: ``scipy.optimize.minimize``. + eps : float + jitter used in the inversion of the covariance matrix for numerical stability + known : dict + optional dict of known parameters (e.g. ``beta0``, ``theta``, ``g``) + init : dict + optional lists of starting values for mle optimization: + - ``theta_init`` initial value of the theta parameters to be optimized over (default to 10% of the range determined with ``lower`` and ``upper``) + - ``g_init`` vector of nugget parameter to be optimized over + covtype : str + covariance kernel type, either ``'Gaussian'``, ``'Matern5_2'`` or ``'Matern3_2'``, see :func: ``~covariance_functions.cov_gen`` + maxit : int + maximum number of iterations for `L-BFGS-B` of :func: ``scipy.optimize.minimize`` dedicated to maximum likelihood optimization + + + + Returns + ------- + None, but fitinfo is updated with maximum likelihood estimates + + """ + f = f.flatten() + model = homGP() + model.mleHomGP( + X=x, + Z=f, + lower=lower, + upper=upper, + known=known, + noiseControl=noiseControl, + init=init, + covtype=covtype, + maxit=maxit, + eps=eps, + settings=settings, + ) + + for key in model.__dict__.keys(): + fitinfo[key] = model.get(key) + fitinfo["is_homGP"] = True + del model + return + + +class homGPWrapper(homGP): + def __init__(self, fitinfo): + for key in fitinfo.keys(): + setattr(self, key, fitinfo[key]) + + +class hetGPWrapper(hetGP): + def __init__(self, fitinfo): + for key in fitinfo.keys(): + setattr(self, key, fitinfo[key]) + + +def GPWrapper(fitinfo): + """ + A method that converts the information in fitinfo (from the fit and predict methods) to a class so + it can be used to make predictions with hetgpy.homGP or hetgpy.hetGP + + """ + if fitinfo["is_homGP"]: + return homGPWrapper(fitinfo) + else: + return hetGPWrapper(fitinfo) + + +def predict(predinfo, fitinfo, x, theta, thetaprime=None, **kwargs): + r""" + Wrapper method for hetgpy.homGP.predict + + Parameters + ---------- + predinfo: dict + (empty) dictionary that will hold prediction results + fitinfo: dict + dictionary with hetgpy.homGP-trained hyperparameters and inverse covariance matrices. fitinfo is converted back into a hetgpy.homGP object for prediction + x: ndarray + nxd numpy array for prediction. Must match same number of columns as supplied to `fitinfo["X0"]` + theta: ndarray + Deprecated, but used to specify output dimension + thetaprime: ndarray + nxd numpy array for calculating covariance matrix + kwargs: dict + additional keyword arguments passed to hetgpy.homGP.predict + + Returns + ------- + None, but predinfo is populated with `mean`, `variance`, and `covmat` fields + """ + GP = fitinfo.get("model") + if GP is None: + # use wrapper class to instantiate trained GP + GP = GPWrapper(fitinfo=fitinfo) + # handle kws + kws = {} + eligible_keys = ["nugs_only", "interval", "interval_lower", "interval_upper"] + for key in eligible_keys: + if key in kwargs.keys(): + kws[key] = kwargs.get("nugs_only") + + preds = GP.predict(x=x, xprime=thetaprime, **kws) + # ensure naming consistency + predinfo["mean"] = preds.get("mean") + predinfo["var"] = preds.get("sd2") + predinfo["nugs"] = preds.get("nugs") + predinfo["covmat"] = preds.get("cov") + del GP + + +def update(fitinfo, x, Y=None, **kwargs): + r""" + Update function for homGP + + Parameters + ---------- + fitinfo: dictionary that contains the fit information for a hetgpy.homGP object + x: array of new design locations + Y: new response. If None, then a kriging believer approach is used to impute the predicted mean at the design location + kwargs: key-value pairs that get passed to hetgpy.homGP.update. + Must be one of: ginit, lower, upper, noiseControl, settings, known, maxit + + Returns + ------- + None, but fitinfo is updated in place + """ + # validate kwargs + valid_kws = ( + "ginit", + "lower", + "upper", + "noiseControl", + "settings", + "known", + "maxit", + ) + for kw in kwargs.keys(): + if kw not in valid_kws: + raise ValueError(f"{kw} not found, must be one of {valid_kws}") + GP = GPWrapper(fitinfo) + if Y is None: + maxit = 0 # impute mean response and do not update hyperparams + Y = GP.predict(x)["mean"] + else: + maxit = kwargs.get("maxit", 100) + kwargs["maxit"] = maxit + GP.update(Xnew=x, Znew=Y, **kwargs) + for key in GP.__dict__.keys(): + fitinfo[key] = GP.get(key) + del GP + return diff --git a/PUQ/surrogatemethods/multihetGP.py b/PUQ/surrogatemethods/multihetGP.py new file mode 100644 index 0000000..508d291 --- /dev/null +++ b/PUQ/surrogatemethods/multihetGP.py @@ -0,0 +1,199 @@ +""" +Set of functions to fit a heteroskedastic GP to each dimension of output data +""" + +import numpy as np +from PUQ.surrogate import emulator + + +def fit( + fitinfo, + x, + theta, + f, + lower=None, + upper=None, + maxit=100, + noiseControl={"k_theta_g_bounds": (1, 100), "g_max": 1e2, "g_bounds": (1e-6, 1)}, + init={}, + known={}, + eps=np.sqrt(np.finfo(float).eps), + settings={ + "linkThetas": "joint", + "logN": True, + "initStrategy": "residuals", + "checkHom": True, + "penalty": True, + "trace": 0, + "return.matrices": True, + "return.hom": False, + "factr": 1e9, + }, + covtype="Gaussian", + **kwargs +): + r""" + Wrapper function for hetgpy.hetGP.mleHetGP + + Arguments + --------- + fitinfo: dictionary/class holding emulator results + x : ndarray_like + matrix of all designs, one per row, or list with elements: + - ``X0`` matrix of unique design locations, one point per row + - ``Z0`` vector of averaged observations, of length ``len(X0)`` + - ``mult`` number of replicates at designs in ``X0``, of length ``len(X0)`` + theta: not used + f : ndarray_like + output array for training. One GP is trained for each output column. + lower,upper : ndarray_like + optional bounds for the ``theta`` parameter (see :func: covariance_functions.cov_gen for the exact parameterization). + In the multivariate case, it is possible to give vectors for bounds (resp. scalars) for anisotropy (resp. isotropy) + noiseControl : dict + dict with elements related to optimization of the noise process parameters: + - ``g_min``, ``g_max`` minimal and maximal noise to signal ratio (of the mean process) + - ``lowerDelta``, ``upperDelta`` optional vectors (or scalars) of bounds on ``Delta``, of length ``len(X0)`` (default to ``np.repeat(eps, X0.shape[0])`` and ``np.repeat(noiseControl["g_max"], X0.shape[0])`` resp., or their ``log``) + - ``lowerpX``, ``upperpX`` optional vectors of bounds of the input domain if `pX` is used. + - ``lowerTheta_g``, ``upperTheta_g`` optional vectors of bounds for the lengthscales of the noise process if ``linkThetas == 'none'``. Same as for ``theta`` if not provided. + - ``k_theta_g_bounds`` if ``linkThetas == 'joint'``, vector with minimal and maximal values for ``k_theta_g`` (default to ``(1, 100)``). See Notes. + - ``g_bounds`` vector for minimal and maximal noise to signal ratios for the noise of the noise process, i.e., the smoothing parameter for the noise process. (default to ``(1e-6, 1)``). + settings : dict + dict for options about the general modeling procedure, with elements: + - ``linkThetas`` defines the relation between lengthscales of the mean and noise processes. Either ``'none'``, ``'joint'``(default) or ``'constr'``, see Notes. + - ``logN``, when ``True`` (default), the log-noise process is modeled. + - ``initStrategy`` one of ``'simple'``, ``'residuals'`` (default) and ``'smoothed'`` to obtain starting values for ``Delta``, see Notes + - ``penalty`` when ``True``, the penalized version of the likelihood is used (i.e., the sum of the log-likelihoods of the mean and variance processes, see References). + - ``hardpenalty`` is ``True``, the log-likelihood from the noise GP is taken into account only if negative (default if ``maxit > 1000``). + - ``checkHom`` when ``True``, if the log-likelihood with a homoskedastic model is better, then return it. + - ``trace`` optional scalar (default to ``0``). If negative, fit silently. If ``0``, only high level information is given. If ``1``, information is given about the result of the heterogeneous model optimization. Level ``2`` gives more details. Level ``3`` additionaly displays all details about initialization of hyperparameters. + - ``return_matrices`` boolean to include the inverse covariance matrix in the object for further use (e.g., prediction). + - ``return_hom`` boolean to include homoskedastic GP models used for initialization (i.e., ``modHom`` and ``modNugs``). + - ``factr`` (default to 1e9) and ``pgtol`` are available to be passed to `options` for L-BFGS-B in :func: ``scipy.optimize.minimize``. + eps : float + jitter used in the inversion of the covariance matrix for numerical stability + init,known : dict + optional lists of starting values for mle optimization or that should not be optimized over, respectively. + Values in ``known`` are not modified, while it can happen to these of ``init``, see Notes. + One can set one or several of the following: + - ``theta`` lengthscale parameter(s) for the mean process either one value (isotropic) or a vector (anistropic) + - ``Delta`` vector of nuggets corresponding to each design in ``X0``, that are smoothed to give ``Lambda`` (as the global covariance matrix depends on ``Delta`` and ``nu_hat``, it is recommended to also pass values for ``theta``) + - ``beta0`` constant trend of the mean process + - ``k_theta_g`` constant used for link mean and noise processes lengthscales, when ``settings['linkThetas'] == 'joint'`` + - ``theta_g`` either one value (isotropic) or a vector (anistropic) for lengthscale parameter(s) of the noise process, when ``settings['linkThetas'] != 'joint'`` + - ``g`` scalar nugget of the noise process + - ``g_H`` scalar homoskedastic nugget for the initialisation with a :func: homGP.mleHomGP. See Notes. + - ``pX`` matrix of fixed pseudo inputs locations of the noise process corresponding to Delta + covtype : str + covariance kernel type, either ``'Gaussian'``, ``'Matern5_2'`` or ``'Matern3_2'``, see :func: ``~covariance_functions.cov_gen`` + maxit : int + maximum number of iterations for `L-BFGS-B` of :func: ``scipy.optimize.minimize`` dedicated to maximum likelihood optimization + """ + numGPs = f.shape[1] + emulist = [dict() for x in range(0, numGPs)] + for i in range(numGPs): + emu = emulator( + x=x, + theta=np.array([[0]]), + f=f[:, i : i + 1], + method="hetGP", + args={ + "noiseControl": noiseControl, + "lower": lower, + "upper": upper, + "settings": settings, + "init": init, + "known": known, + "covtype": covtype, + "maxit": maxit, + "eps": eps, + }, + ) + emulist[i] = emu + fitinfo["f"] = f.T + fitinfo["theta"] = x + fitinfo["emulist"] = emulist + fitinfo["numGPs"] = numGPs + return + + +def predict(predinfo, fitinfo, x, theta, thetaprime, **kws): + r""" + Wrapper method for hetGP.predict + + Parameters + ---------- + predinfo: dict + (empty) dictionary that will hold prediction results + fitinfo: dict + dictionary with hetgpy.hetGP-trained hyperparameters and inverse covariance matrices. fitinfo is converted back into a hetgpy.hetGP object for prediction + x: ndarray + nxd numpy array for prediction. Must match same number of columns as supplied to `fitinfo["X0"]` + theta: ndarray + Deprecated, but used to specify output dimension + thetaprime: ndarray + nxd numpy array for calculating covariance matrix + kwargs: dict + additional keyword arguments passed to hetgpy.hetGP.predict + + Returns + ------- + None, but predinfo is populated with `mean`, `variance`,`nugs`, and `covmat` fields. + `mean`, `variance`, and `nugs` are stored as ndarrays, with each column corresponding to the prediction for the ith output column. + `covmat` is stored as a list of matrices with the ith element corresponding to the ith output column + """ + numGPs = fitinfo["numGPs"] + emulist = [dict() for x in range(0, numGPs)] + + # instantiate outputs + nr, nc = (x.shape[0], numGPs) + for key in ("mean", "var", "nugs"): + predinfo[key] = np.zeros(shape=(nc, nr), dtype=float) + + if thetaprime is None: + predinfo["covmat"] = np.zeros(shape=(nc, nr, nr), dtype=float) + else: + predinfo["covmat"] = np.zeros(shape=(nc, nr, thetaprime.shape[0]), dtype=float) + + for i in range(numGPs): + preds = fitinfo["emulist"][i].predict(x=x, thetaprime=thetaprime) + predinfo["mean"][i, :] = preds._info["mean"] + predinfo["var"][i, :] = preds._info["var"] + predinfo["nugs"][i, :] = preds._info["nugs"] + predinfo["covmat"][i, :, :] = preds._info["covmat"] + + predinfo["S"] = np.full((numGPs, numGPs, x.shape[0]), np.nan) + predinfo["R"] = np.full((numGPs, numGPs, x.shape[0]), np.nan) + for i in range(0, x.shape[0]): + C = np.diag(predinfo["var"][:, i]) + R = np.diag(predinfo["nugs"][:, i]) + predinfo["S"][:, :, i] = C + predinfo["R"][:, :, i] = R + + return + + +def update(fitinfo, x, Y=None, **kwargs): + r""" + Update function for hetGP + + Parameters + ---------- + fitinfo: dictionary that contains the fit information for a hetgpy.hetGP object + x: array of new design locations + Y: new response. If None, then a kriging believer approach is used to impute the predicted mean at the design location + kwargs: key-value pairs that get passed to hetgpy.hetGP.update. + Must be one of: ginit, lower, upper, noiseControl, settings, known, maxit + + Returns + ------- + None, but fitinfo is updated in place and individual emulator are accessed via fitinfo["emulist"] + """ + numGPs = fitinfo["numGPs"] + for i in range(numGPs): + emu = fitinfo["emulist"][i] + if Y is not None: + Yi = Y[:, i] + else: + Yi = None + emu.update(x=x, Y=Yi, **kwargs) + return diff --git a/PUQ/surrogatemethods/multihomGP.py b/PUQ/surrogatemethods/multihomGP.py new file mode 100644 index 0000000..ed00e8d --- /dev/null +++ b/PUQ/surrogatemethods/multihomGP.py @@ -0,0 +1,169 @@ +""" +Set of functions to fit a homoskedastic GP to each dimension of output data +""" + +import numpy as np +from PUQ.surrogate import emulator + + +def fit( + fitinfo, + x, + theta, + f, + lower=None, + upper=None, + known={}, + noiseControl={"g_bounds": [np.sqrt(np.finfo(float).eps), 100]}, + init={}, + covtype="Gaussian", + maxit=100, + eps=np.sqrt(np.finfo(float).eps), + settings={"return.Ki": True, "factr": 1e7}, + **kwargs +): + r""" + Fit a homoskedastic GP to each dimension of output data. + + Parameters + ---------- + fitinfo: dictionary/class of results + x: ndarray + nxd design matrix (must have at least one column) + f: ndarray + output array for training. One GP is trained for each output column. + theta: not used + lower,upper : ndarray_like + optional bounds for the ``theta`` parameter (see :func: covariance_functions.cov_gen for the exact parameterization). + In the multivariate case, it is possible to give vectors for bounds (resp. scalars) for anisotropy (resp. isotropy) + noiseControl : dict + dict with element: + - ``g_bounds`` vector providing minimal and maximal noise to signal ratio (default to ``(sqrt(MACHINE_DOUBLE_EPS), 100)``). + settings : dict + dict for options about the general modeling procedure, with elements: + - ``return_Ki`` boolean to include the inverse covariance matrix in the object for further use (e.g., prediction). + - ``factr`` (default to 1e7) and ``pgtol`` are available to be passed to `options` for L-BFGS-B in :func: ``scipy.optimize.minimize``. + eps : float + jitter used in the inversion of the covariance matrix for numerical stability + known : dict + optional dict of known parameters (e.g. ``beta0``, ``theta``, ``g``) + init : dict + optional lists of starting values for mle optimization: + - ``theta_init`` initial value of the theta parameters to be optimized over (default to 10% of the range determined with ``lower`` and ``upper``) + - ``g_init`` vector of nugget parameter to be optimized over + covtype : str + covariance kernel type, either ``'Gaussian'``, ``'Matern5_2'`` or ``'Matern3_2'``, see :func: ``~covariance_functions.cov_gen`` + maxit : int + maximum number of iterations for `L-BFGS-B` of :func: ``scipy.optimize.minimize`` dedicated to maximum likelihood optimization + + Returns + ------- + None, but fitinfo is updated with maximum likelihood estimates. Individual emulators are accessed via fitinfo["emulist"] + + """ + + numGPs = f.shape[1] + emulist = [dict() for x in range(0, numGPs)] + for i in range(numGPs): + emu = emulator( + x=x, + theta=np.array([[i]]), + f=f[:, i : i + 1], + method="homGP", + args={ + "noiseControl": noiseControl, + "lower": lower, + "upper": upper, + "settings": settings, + "init": init, + "known": known, + "covtype": covtype, + "maxit": maxit, + "eps": eps, + }, + ) + emulist[i] = emu + fitinfo["f"] = f + fitinfo["emulist"] = emulist + fitinfo["numGPs"] = numGPs + return + + +def predict(predinfo, fitinfo, x, theta, thetaprime, **kws): + r""" + Wrapper method for homGP.predict + + Parameters + ---------- + predinfo: dict + (empty) dictionary that will hold prediction results + fitinfo: dict + dictionary with hetgpy.homGP-trained hyperparameters and inverse covariance matrices. fitinfo is converted back into a hetgpy.homGP object for prediction + x: ndarray + nxd numpy array for prediction. Must match same number of columns as supplied to `fitinfo["X0"]` + theta: ndarray + Deprecated, but used to specify output dimension + thetaprime: ndarray + nxd numpy array for calculating covariance matrix + kwargs: dict + additional keyword arguments passed to hetgpy.homGP.predict + + Returns + ------- + None, but predinfo is populated with `mean`, `variance`, and `covmat` fields. + `mean` and `variance` are stored as ndarrays, with each column corresponding to the prediction for the ith output column. + `covmat` is stored as a list of matrices with the ith element corresponding to the ith output column + """ + numGPs = fitinfo["numGPs"] + emulist = [dict() for x in range(0, numGPs)] + + # instantiate outputs + nr, nc = (x.shape[0], numGPs) + for key in ("mean", "var", "nugs"): + predinfo[key] = np.zeros(shape=(nc, nr), dtype=float) + + if thetaprime is None: + predinfo["covmat"] = np.zeros(shape=(nc, nr, nr), dtype=float) + else: + predinfo["covmat"] = np.zeros(shape=(nc, nr, thetaprime.shape[0]), dtype=float) + + for i in range(numGPs): + preds = fitinfo["emulist"][i].predict(x=x, thetaprime=thetaprime) + predinfo["mean"][i, :] = preds._info["mean"] + predinfo["var"][i, :] = preds._info["var"] + predinfo["nugs"][i, :] = preds._info["nugs"] + predinfo["covmat"][i, :, :] = preds._info["covmat"] + + predinfo["S"] = np.full((numGPs, numGPs, x.shape[0]), np.nan) + for i in range(0, x.shape[0]): + C = np.diag(predinfo["var"][:, i]) + predinfo["S"][:, :, i] = C + + return + + +def update(fitinfo, x, Y=None, **kwargs): + r""" + Update function for homGP + + Parameters + ---------- + fitinfo: dictionary that contains the fit information for a hetgpy.homGP object + x: array of new design locations + Y: new response. If None, then a kriging believer approach is used to impute the predicted mean at the design location + kwargs: key-value pairs that get passed to hetgpy.homGP.update. + Must be one of: ginit, lower, upper, noiseControl, settings, known, maxit + + Returns + ------- + None, but fitinfo is updated in place and individual emulator are accessed via fitinfo["emulist"] + """ + numGPs = fitinfo["numGPs"] + for i in range(numGPs): + emu = fitinfo["emulist"][i] + if Y is not None: + Yi = Y[:, i] + else: + Yi = None + emu.update(x=x, Y=Yi, **kwargs) + return diff --git a/PUQ/surrogatesupport/__init__.py b/PUQ/surrogatesupport/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/PUQ/surrogatesupport/matern_covmat.pyx b/PUQ/surrogatesupport/matern_covmat.pyx deleted file mode 100644 index 4c82212..0000000 --- a/PUQ/surrogatesupport/matern_covmat.pyx +++ /dev/null @@ -1,60 +0,0 @@ - -import numpy as np -cimport numpy as np -from cpython cimport array -from libc.stdio cimport printf -from libc.math cimport fabs -cimport cython - -@cython.boundscheck(False) -@cython.wraparound(False) -def map4(double[:] x1, double[:] x2, double[:,:] s, double[:,:] r, double g): - m = s.shape[0] - n = s.shape[1] - with cython.nogil: - for i in range(m): - for j in range(n): - s[i,j] = x1[i] - x2[j] - r[i,j] = -g * s[i,j] - s[i,j] = fabs(s[i,j]) - r[i,j] /= (1 + s[i, j]) - - -def covmat(x1, x2, gammav, return_gradhyp=False, return_gradx1=False): - """Return the covariance between x1 and x2 given parameter gammav.""" - x1 = x1.reshape(1, gammav.shape[0]-1)/np.exp(gammav[:-1]) \ - if x1.ndim < 1.5 else x1/np.exp(gammav[:-1]) - x2 = x2.reshape(1, gammav.shape[0]-1)/np.exp(gammav[:-1]) \ - if x2.ndim < 1.5 else x2/np.exp(gammav[:-1]) - - V = np.zeros([x1.shape[0], x2.shape[0]]) - R = np.full((x1.shape[0], x2.shape[0]), 1/(1+np.exp(gammav[-1]))) - S = np.zeros([x1.shape[0], x2.shape[0]]) - - if return_gradhyp: - dR = np.zeros([gammav.shape[0], x1.shape[0], x2.shape[0]]) - elif return_gradx1: - dR = np.zeros([ x1.shape[1], x1.shape[0], x2.shape[0]]) - for k in range(0, gammav.shape[0]-1): - if return_gradx1: - map4(x1[:, k],x2[:, k], S, dR[k], np.exp(-gammav[k])) - else: - S = np.abs(np.subtract.outer(x1[:, k], x2[:, k])) - R *= (1 + S) - V -= S - if return_gradhyp: - dR[k] = (S ** 2) / (1 + S) - if return_gradhyp or return_gradx1: - dR = dR.transpose(1,2,0) - R *= np.exp(V) - if return_gradhyp: - dR *= R[:, :, None] - dR[:, :, -1] = np.exp(gammav[-1]) / ((1 + np.exp(gammav[-1]))) *\ - (1 / (1 + np.exp(gammav[-1])) - R) - elif return_gradx1: - dR *= R[:, :, None] - R += np.exp(gammav[-1])/(1+np.exp(gammav[-1])) - if return_gradhyp or return_gradx1: - return R, dR - else: - return R \ No newline at end of file diff --git a/README.rst b/README.rst index 21db17e..1a6eb17 100644 --- a/README.rst +++ b/README.rst @@ -28,9 +28,9 @@ Dependencies PUQ is a Python package that employs novel experimental design techniques with intelligent selection criteria, refining data collection to enhance the efficiency and effectiveness of uncertainty quantification. -This code is tested with Python 3.9, 3.10, 3.11, and 3.12 and requires pip. +This code is tested with Python 3.10, 3.11, and 3.12 and requires pip. -Python 3.12 users need to install setuptools manually via:: +Users may need to install setuptools manually via:: python -m ensurepip --upgrade python -m pip install --upgrade setuptools @@ -68,7 +68,11 @@ To install the PUQ package: pip install -r requirements.txt -3) From the command line, use the following command to install PUQ:: +3) Install the latest development version of hetGPy:: + + python -m pip install git+https://github.com/davidogara/hetGPy.git + +4) From the command line, use the following command to install PUQ:: pip install -e . @@ -80,9 +84,9 @@ Testing The test suite requires the pytest_ and pytest-cov_ packages, which can be installed via ``pip install pytest pytest-cov``. -The test suite can be run from the ``tests/`` directory of the source distribution by running:: +The test suite can be run from the main directory of the source distribution by running:: -./run-tests.sh + pytest tests/ Documentation @@ -105,12 +109,12 @@ The HTML files are then stored in ``docs/html`` .. code-block:: bibtex - @techreport{PUQ2022, - author = {Özge Sürer, Matthew Plumlee, Stefan M. Wild}, + @techreport{PUQ2025, + author = {Özge Sürer, David O'Gara, Matthew Plumlee, Stefan M. Wild}, title = {PUQ Users Manual}, institution = {}, - number = {Version 0.1.0}, - year = {2022}, + number = {Version 0.1.1}, + year = {2025}, url = {https://github.com/parallelUQ/PUQ} } diff --git a/docs/conf.py b/docs/conf.py index 832d7a1..870b02b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,11 +21,11 @@ # -- Project information ----------------------------------------------------- project = "PUQ" -copyright = "2024, Özge Sürer, Matthew Plumlee, Stefan M. Wild" -author = "Özge Sürer, Matthew Plumlee, Stefan M. Wild" +copyright = "2025, Özge Sürer, David O'Gara, Matthew Plumlee, Stefan M. Wild" +author = "Özge Sürer, David O'Gara, Matthew Plumlee, Stefan M. Wild" # The full version, including alpha/beta/rc tags -release = "0.1" +release = "0.1.1" needs_sphinx = "3.0" # -- General configuration --------------------------------------------------- diff --git a/examples/Example1/README.rst b/examples/Example1/README.rst new file mode 100644 index 0000000..805bfcf --- /dev/null +++ b/examples/Example1/README.rst @@ -0,0 +1,29 @@ + +Examples +~~~~~~~~ + +This example demonstrates how to apply the proposed method from Sürer, Plumlee, and Wild (2024), +Sequential Bayesian Experimental Design for Calibration of Expensive Simulation Models, +to a deterministic simulation model with high-dimensional outputs. + + +**Instructions for running the illustrative examples** + +To replicate the figures below, respectively: + +1) Go to the ``examples/Example1`` directory. + +2) Execute the followings from the command line:: + + python example.py + +Running this script should not take more than 120 sec. See the figures (png files) saved under the directory. + +.. image:: ex1.png + :alt: Illustration of PUQ with the example + :align: center + :width: 600 + +Blue circles denote the initial design, and plus markers indicate the acquired +points obtained via random sampling from the prior (left), variance (middle), and proposed +integrated variance (IVAR) acquisition functions (right). \ No newline at end of file diff --git a/examples/Example1/ex1.png b/examples/Example1/ex1.png new file mode 100644 index 0000000..fb9ec66 Binary files /dev/null and b/examples/Example1/ex1.png differ diff --git a/examples/Example1/example.py b/examples/Example1/example.py new file mode 100644 index 0000000..b27fd44 --- /dev/null +++ b/examples/Example1/example.py @@ -0,0 +1,87 @@ +import numpy as np +import matplotlib.pyplot as plt +import scipy.stats as sps +from scipy.stats import qmc +from PUQ.prior import prior_dist +from PUQ.designmethods.sequential_md_deterministic import sequential_design +from test_func import unimodal + +if __name__ == "__main__": + + cex = unimodal() + + # # # Create a mesh for test set # # # + xpl = np.linspace(cex.thetalimits[0][0], cex.thetalimits[0][1], 50) + ypl = np.linspace(cex.thetalimits[1][0], cex.thetalimits[1][1], 50) + Xpl, Ypl = np.meshgrid(xpl, ypl) + thetatest = np.vstack([Xpl.ravel(), Ypl.ravel()]).T + ftest = np.zeros((thetatest.shape[0], 1)) + for i in range(thetatest.shape[0]): + ftest[i, 0] = cex.function(thetatest[i, 0], thetatest[i, 1]) + + ptest = sps.norm.pdf(cex.real_data - ftest, 0, np.sqrt(cex.obsvar)) + + test_data = {"theta": thetatest, "f": ftest, "p": ptest, "p_prior": 1} + # # # # # # # # # # # # # # # # # # # # # + prior_func = prior_dist(dist="uniform")( + a=cex.thetalimits[:, 0], b=cex.thetalimits[:, 1] + ) + + # Initial sample + n0, s = 10, 2 + + ndim = cex.thetalimits.shape[0] + sampler = qmc.LatinHypercube(d=ndim, seed=s) + # Generate samples in [0,1]^d + unit_sample = sampler.random(n=n0) + # Scale using limits + t0 = qmc.scale(unit_sample, cex.thetalimits[:, 0], cex.thetalimits[:, 1]) + + f0 = np.zeros((t0.shape[0], 1)) + for i in range(t0.shape[0]): + f0[i, 0] = cex.function(t0[i, 0], t0[i, 1]) + + fig, ax = plt.subplots(1, 3, figsize=(12, 4)) + for i, af in enumerate(["rnd", "var", "ivar"]): + des_obj = sequential_design(cex) + des_obj.build_design( + z0=t0, + f0=f0, + T=50, + test=test_data, + af=af, + args={ + "mini_batch": 1, + "n_init_thetas": 10, + "nworkers": 2, + "seed_n0": 1, + "prior": prior_func, + "data_test": None, + "max_evals": 60, + "type_init": None, + "seed": 0, + "integral": "LHS", + }, + ) + + theta_al = des_obj.zs + + cp = ax[i].contour(Xpl, Ypl, ptest.reshape(50, 50), 20, cmap="RdGy") + ax[i].scatter( + theta_al[n0:, 0], theta_al[n0:, 1], c="black", marker="+", zorder=2 + ) + ax[i].scatter( + theta_al[0:n0, 0], + theta_al[0:n0, 1], + zorder=2, + marker="o", + facecolors="none", + edgecolors="blue", + ) + ax[i].set_xlabel(r"$\theta_1$", fontsize=16) + if i == 0: + ax[i].set_ylabel(r"$\theta_2$", fontsize=16) + + ax[i].tick_params(axis="both", labelsize=16) + plt.savefig("ex1.png", format="jpeg", bbox_inches="tight", dpi=1000) + plt.show() diff --git a/examples/Example1/example_2d.py b/examples/Example1/example_2d.py new file mode 100644 index 0000000..77fbeba --- /dev/null +++ b/examples/Example1/example_2d.py @@ -0,0 +1,98 @@ +import numpy as np +import scipy.stats as sps +from scipy.stats import qmc +from PUQ.designmethods.sequential_md_deterministic import sequential_design +from PUQ.prior import prior_dist +from test_func import unimodal, banana, bimodal, unidentifiable +import matplotlib.pyplot as plt + +FUNCNAME = "banana" + +if __name__ == "__main__": + + print("Running function: " + FUNCNAME) + + cex = eval(FUNCNAME)() + + # Create a mesh for test set + xpl = np.linspace(cex.thetalimits[0][0], cex.thetalimits[0][1], 50) + ypl = np.linspace(cex.thetalimits[1][0], cex.thetalimits[1][1], 50) + Xpl, Ypl = np.meshgrid(xpl, ypl) + thetatest = np.vstack([Xpl.ravel(), Ypl.ravel()]).T + ftest = np.zeros((thetatest.shape[0], cex.d)) + for i in range(thetatest.shape[0]): + ftest[i, :] = cex.function(thetatest[i, 0], thetatest[i, 1]) + + ptest = np.zeros(thetatest.shape[0]) + if cex.data_name == "unimodal": + ptest = sps.norm.pdf(cex.real_data - ftest, 0, np.sqrt(cex.obsvar)) + else: + for i in range(ftest.shape[0]): + mean = ftest[i, :] + rnd = sps.multivariate_normal(mean=mean, cov=cex.obsvar) + ptest[i] = rnd.pdf(cex.real_data) + test_data = {"theta": thetatest, "f": ftest, "p": ptest, "p_prior": 1} + + # Set a uniform prior + prior_func = prior_dist(dist="uniform")( + a=cex.thetalimits[:, 0], b=cex.thetalimits[:, 1] + ) + + # Define acquisition functions + # acq_funcs = ["eivar", "rnd", "maxvar", "maxexp"] + acq_funcs = ["ivar", "rnd", "var"] + datalist = [] + rep_no, n0 = 2, 10 + + # Run over 50 replications + for seed_id in range(1, rep_no + 1): + # Initial sample + ndim = cex.thetalimits.shape[0] + sampler = qmc.LatinHypercube(d=ndim, seed=seed_id) + # Generate samples in [0,1]^d + unit_sample = sampler.random(n=n0) + # Scale using limits + t0 = qmc.scale(unit_sample, cex.thetalimits[:, 0], cex.thetalimits[:, 1]) + f0 = np.zeros((t0.shape[0], cex.d)) + for i in range(t0.shape[0]): + f0[i, :] = cex.function(t0[i, 0], t0[i, 1]) + + for func in acq_funcs: + print("Running " + func + " with seed " + str(seed_id)) + des_obj = sequential_design(cex) + des_obj.build_design( + z0=t0, + f0=f0, + T=75, + test=test_data, + af=func, + args={ + "mini_batch": 1, + "n_init_thetas": 10, + "nworkers": 2, + "prior": prior_func, + "data_test": test_data, + "seed": seed_id, + "integral": "LHS", + }, + ) + + theta_al = des_obj.zs + + fig, ax = plt.subplots() + cp = ax.contour(Xpl, Ypl, ptest.reshape(50, 50), 20, cmap="RdGy") + ax.scatter( + theta_al[n0:, 0], theta_al[n0:, 1], c="black", marker="+", zorder=2 + ) + ax.scatter( + theta_al[0:n0, 0], + theta_al[0:n0, 1], + zorder=2, + marker="o", + facecolors="none", + edgecolors="blue", + ) + ax.set_xlabel(r"$\theta_1$", fontsize=16) + ax.set_ylabel(r"$\theta_2$", fontsize=16) + ax.tick_params(axis="both", labelsize=16) + plt.show() diff --git a/examples/Technometrics2024/test_funcs.py b/examples/Example1/test_func.py similarity index 70% rename from examples/Technometrics2024/test_funcs.py rename to examples/Example1/test_func.py index 15bdb6f..9759adb 100644 --- a/examples/Technometrics2024/test_funcs.py +++ b/examples/Example1/test_func.py @@ -13,6 +13,9 @@ def __init__(self): self.p = 2 self.x = np.arange(0, self.d)[:, None] self.real_x = np.arange(0, self.d)[:, None] + #### + self.dx = 1 + self.dt = 2 def function(self, theta1, theta2): """ @@ -23,16 +26,6 @@ def function(self, theta1, theta2): f = (thetas @ S) @ thetas.T return f - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the simulator - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - class banana: def __init__(self): @@ -45,21 +38,14 @@ def __init__(self): self.d = 2 self.x = np.arange(0, self.d)[:, None] self.real_x = np.arange(0, self.d)[:, None] + #### + self.dx = 1 + self.dt = 1 def function(self, theta1, theta2): f = np.array([theta1, theta2 + 0.03 * theta1**2]) return f - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the banana function - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - class unidentifiable: def __init__(self): @@ -72,21 +58,14 @@ def __init__(self): self.p = 2 self.x = np.arange(0, self.d)[:, None] self.real_x = np.arange(0, self.d)[:, None] + #### + self.dx = 1 + self.dt = 1 def function(self, theta1, theta2): f = np.array([theta1, theta2]) return f - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the unidentifiable function - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - class bimodal: def __init__(self): @@ -99,21 +78,14 @@ def __init__(self): self.p = 2 self.x = np.arange(0, self.d)[:, None] self.real_x = np.arange(0, self.d)[:, None] + #### + self.dx = 1 + self.dt = 1 def function(self, theta1, theta2): f = np.array([theta2 - theta1**2, theta2 - theta1]) return f - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the bimodal function - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - def create_test_data(al_test, cls_func): ftest = al_test._info["f"] diff --git a/examples/Technometrics2024/figure2.py b/examples/Example1/toy_example.py similarity index 71% rename from examples/Technometrics2024/figure2.py rename to examples/Example1/toy_example.py index 7029f5a..a8fb412 100644 --- a/examples/Technometrics2024/figure2.py +++ b/examples/Example1/toy_example.py @@ -2,7 +2,7 @@ import numpy as np import matplotlib.pyplot as plt from PUQ.surrogate import emulator -from PUQ.posterior import posterior +from PUQ.posterior import multiple_pdfs, compute_postvar class sinlinear: @@ -42,21 +42,45 @@ def sim(self, H, persis_info, sim_specs, libE_info): ptr = sps.norm.pdf(cls_sinlin.real_data, f, np.sqrt(cls_sinlin.obsvar)) # Fit an emulator - emu = emulator(cls_sinlin.x, theta, f, method="PCGP") + # emu = emulator(cls_sinlin.x, theta, f, method="PCGP") - post = posterior(data_cls=cls_sinlin, emulator=emu) + emu = emulator(x=theta, theta=cls_sinlin.x, f=f, method="multihomGP") # Generate test data thetatest = np.arange(-10, 10, 0.0025)[:, None] ftest = cls_sinlin.function(thetatest) ptest = sps.norm.pdf(cls_sinlin.real_data, ftest, np.sqrt(cls_sinlin.obsvar)) - # Predict via the emulator - emupred_test = emu.predict(x=cls_sinlin.x, theta=thetatest) - emupred_tr = emu.predict(x=cls_sinlin.x, theta=theta) + # predict at mesh + nm, d = thetatest.shape[0], cls_sinlin.d + pr_test = emu.predict(x=thetatest, thetaprime=thetatest) + mu, Sn = pr_test._info["mean"], pr_test._info["S"] + muT = mu.reshape(nm, d) + S = Sn.transpose(2, 0, 1) + Sigma3d = cls_sinlin.obsvar.reshape(1, cls_sinlin.d, cls_sinlin.d) + N = S + Sigma3d + M = S + 0.5 * Sigma3d + posttesthat = multiple_pdfs(cls_sinlin.real_data, muT, N) + + diags = np.diag(cls_sinlin.obsvar[cls_sinlin.x, cls_sinlin.x.T]) + coef = ( + (2**cls_sinlin.d) * (np.sqrt(np.pi) ** cls_sinlin.d) * np.sqrt(np.prod(diags)) + ) + posttestvar = compute_postvar(cls_sinlin.real_data, muT, N, M, coef) + + # predict at mesh + nm, d = theta.shape[0], cls_sinlin.d + pr_test = emu.predict(x=theta, thetaprime=theta) + mu, Sn = pr_test._info["mean"], pr_test._info["S"] + muT = mu.reshape(nm, d) + S = Sn.transpose(2, 0, 1) + Sigma3d = cls_sinlin.obsvar.reshape(1, cls_sinlin.d, cls_sinlin.d) + N = S + Sigma3d + posttrhat = multiple_pdfs(cls_sinlin.real_data, muT, N) - posttesthat, posttestvar = post.predict(thetatest) - posttrhat, posttrvar = post.predict(theta) + # Predict via the emulator + emupred_test = emu.predict(x=thetatest) + emupred_tr = emu.predict(x=theta) # Figure 2 (a) ft = 20 @@ -99,9 +123,9 @@ def sim(self, H, persis_info, sim_specs, libE_info): # Figure 2 (c) # Fit an emulator for posterior - emu = emulator(cls_sinlin.x, theta, ptr, method="PCGP") + emu = emulator(theta=cls_sinlin.x, x=theta, f=ptr, method="multihomGP") - emupred_test = emu.predict(x=cls_sinlin.x, theta=thetatest) + emupred_test = emu.predict(x=thetatest) emumean_test = emupred_test.mean() emumean_var = emupred_test.var() diff --git a/examples/Example2/README.rst b/examples/Example2/README.rst new file mode 100644 index 0000000..6d88e29 --- /dev/null +++ b/examples/Example2/README.rst @@ -0,0 +1,32 @@ + +Examples +~~~~~~~~ + +This example demonstrates how to apply the proposed method from Sürer (2024), +Simulation Experiment Design for Calibration via Active Learning, to a deterministic simulation model with one-dimensional outputs. + + +Instructions for running the illustrative examples +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To replicate the figures below, respectively: + +1) Go to the ``examples/Example2`` directory. + +2) Execute the followings from the command line:: + + python example.py + +Running this script should not take more than 60 sec. See the figures (png files) saved under the directory. + + +.. image:: ex2.png + :alt: Illustration of PUQ with the example + :align: center + :width: 600 + + +The left panel shows simulation model outputs across design inputs (x) at four +different parameter values. Black dots represent field data observed at five +equally spaced design inputs. The right panel displays the points acquired +using the proposed acquisition function. diff --git a/examples/Example2/ex2.png b/examples/Example2/ex2.png new file mode 100644 index 0000000..945db56 Binary files /dev/null and b/examples/Example2/ex2.png differ diff --git a/examples/Example2/example.py b/examples/Example2/example.py new file mode 100644 index 0000000..2e897ec --- /dev/null +++ b/examples/Example2/example.py @@ -0,0 +1,79 @@ +import numpy as np +from ptest_funcs import sinfunc +import matplotlib.pyplot as plt +from scipy.stats import qmc +from PUQ.designmethods.sequential_1d_deterministic import sequential_design + + +if __name__ == "__main__": + s = 1 + cex = sinfunc() + dt = len(cex.true_theta) + x_obs = np.array([0.1, 0.1, 0.3, 0.3, 0.5, 0.5, 0.7, 0.7, 0.9, 0.9])[:, None] + cex.realdata(x=x_obs, seed=s) + + fig, ax = plt.subplots(1, 2, figsize=(12, 4)) + s = 1 + th_vec = [np.pi / 7, np.pi / 6, np.pi / 5, np.pi / 4] + thlabel = [7, 6, 5, 4] + x_vec = (np.arange(0, 100, 1) / 100)[:, None] + fvec = np.zeros((len(th_vec), len(x_vec))) + colors = ["blue", "orange", "red", "green", "purple"] + for t_id, t in enumerate(th_vec): + for x_id, x in enumerate(x_vec): + fvec[t_id, x_id] = cex.function(x, t)[0] + ax[0].plot( + x_vec, + fvec[t_id, :], + label=r"$\theta=\pi/$" + str(thlabel[t_id]), + color=colors[t_id], + linewidth=3, + ) + for d_id in range(len(cex.x)): + ax[0].scatter(cex.x[d_id, 0], cex.real_data[0, d_id], color="black", s=50) + ft = 16 + ax[0].set_xlabel(r"$x$", fontsize=ft) + ax[0].set_ylabel(r"$\eta(x, \theta)$", fontsize=ft) + ax[0].set_xticks([0.1, 0.3, 0.5, 0.7, 0.9], [0.1, 0.3, 0.5, 0.7, 0.9]) + ax[0].tick_params(labelsize=ft - 2) + ax[0].legend(bbox_to_anchor=(1.1, -0.2), fontsize=ft - 2, ncol=4) + + # Generate initial sample + n0, nmax = 10, 30 + ndim = cex.zlim.shape[0] + sampler = qmc.LatinHypercube(d=ndim, seed=s) + z0 = sampler.random(n=n0) + f0 = np.array([cex.function(z0[i, 0], z0[i, 1]) for i in range(n0)]) + + # Generate design + des_obj = sequential_design(cex) + des_obj.build_design( + z0=z0, + f0=f0[:, None], + T=nmax, + af="ivar", + args={"nL": 200, "seed": s, "integral": "LHS"}, + ) + + ax[1].scatter( + des_obj.zs[n0:, 0], des_obj.zs[n0:, 1], marker="+", c="red", s=200, linewidth=3 + ) + ax[1].hlines( + cex.true_theta, + 0, + 1, + linestyles="dotted", + linewidth=3, + colors="orange", + ) + for xitem in x_obs: + ax[1].vlines( + xitem, 0, 1, linestyles="dotted", colors="orange", linewidth=3, zorder=1 + ) + ax[1].set_xlabel(r"$x$", fontsize=ft) + ax[1].set_ylabel(r"$\theta$", fontsize=ft) + ax[1].tick_params(labelsize=ft) + ax[1].set_xlim(0, 1) + ax[1].set_ylim(0, 1) + plt.savefig("ex2.png", format="jpeg", bbox_inches="tight", dpi=1000) + plt.show() diff --git a/examples/JQT2024/ptest_funcs.py b/examples/Example2/ptest_funcs.py similarity index 92% rename from examples/JQT2024/ptest_funcs.py rename to examples/Example2/ptest_funcs.py index 6bcf41f..afbc110 100644 --- a/examples/JQT2024/ptest_funcs.py +++ b/examples/Example2/ptest_funcs.py @@ -4,12 +4,13 @@ class sinfunc: def __init__(self): self.data_name = "sinfunc" - self.thetalimits = np.array([[0, 1], [0, 1]]) + self.zlim = np.array([[0, 1], [0, 1]]) self.true_theta = np.array([np.pi / 5]) self.out = [("f", float)] self.d = 1 self.p = 2 self.dx = 1 + self.dt = 1 self.x = None self.real_data = None self.sigma2 = 0.2**2 @@ -19,14 +20,9 @@ def function(self, x, theta): f = np.sin(10 * x - 5 * theta) return f - def sim(self, H, persis_info, sim_specs, libE_info): - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - return H_o, persis_info - def realdata(self, x, seed, isbias=False): self.x = x + self.d = len(x) self.nodata = False self.obsvar = np.diag(np.repeat(self.sigma2, len(self.x))) @@ -38,19 +34,9 @@ def realdata(self, x, seed, isbias=False): self.real_data = np.array([fevals], dtype="float64") def genobsdata(self, x, isbias=False): - if isbias: - return ( - self.function(x[0], self.true_theta[0]) - + self.bias(x[0]) - + np.random.normal(0, np.sqrt(self.sigma2), 1) - ) - else: - return self.function(x[0], self.true_theta[0]) + np.random.normal( - 0, np.sqrt(self.sigma2), 1 - ) - - def bias(self, x): - return 1 - (1 / 3) * x - (2 / 3) * (x**2) + return self.function(x[0], self.true_theta[0]) + np.random.normal( + 0, np.sqrt(self.sigma2), 1 + ) class pritam: diff --git a/examples/Example3/README.rst b/examples/Example3/README.rst new file mode 100644 index 0000000..9ee4329 --- /dev/null +++ b/examples/Example3/README.rst @@ -0,0 +1,31 @@ +Examples +~~~~~~~~ + +This example demonstrates how to use the performance model from Sürer and Wild (2024), +An Active Learning Performance Model for Parallel Bayesian Calibration of Expensive Simulations. + + +**Instructions for running the illustrative examples with performance model** + +To replicate the figures below, respectively: + +1) Go to the ``examples/Example3`` directory. + +2) Execute any of the following from the command line: + +.. code-block:: python + + python example_a.py + python example_b.py + +Running each script should not take more than 60 sec. See the figures (png files) saved under ``examples/Example3`` directory. + +.. image:: ex3_a.png + :alt: Illustration of PUQ with the example + :align: center + :width: 600 + +.. image:: ex3_b.png + :alt: Illustration of PUQ with the example + :align: center + :width: 600 diff --git a/examples/Example3/ex3_a.png b/examples/Example3/ex3_a.png new file mode 100644 index 0000000..0dd5e60 Binary files /dev/null and b/examples/Example3/ex3_a.png differ diff --git a/examples/Example3/ex3_b.png b/examples/Example3/ex3_b.png new file mode 100644 index 0000000..76118a9 Binary files /dev/null and b/examples/Example3/ex3_b.png differ diff --git a/examples/IJOC2024+/Workshop_files/WS1_synth.py b/examples/Example3/example_a.py similarity index 97% rename from examples/IJOC2024+/Workshop_files/WS1_synth.py rename to examples/Example3/example_a.py index 5b7e8a3..3893083 100644 --- a/examples/IJOC2024+/Workshop_files/WS1_synth.py +++ b/examples/Example3/example_a.py @@ -88,5 +88,5 @@ ax[sid].tick_params(axis="both", which="major", labelsize=ft - 5) fig.suptitle("Simulation Time Increases \u2192", fontsize=ft) fig.tight_layout() -plt.savefig("Figure_WS_1.jpg", format="jpeg", bbox_inches="tight", dpi=500) +plt.savefig("ex3_a.png", format="jpeg", bbox_inches="tight", dpi=500) plt.show() diff --git a/examples/IJOC2024+/Workshop_files/WS2_synth.py b/examples/Example3/example_b.py similarity index 98% rename from examples/IJOC2024+/Workshop_files/WS2_synth.py rename to examples/Example3/example_b.py index 2b40347..ef1ec44 100644 --- a/examples/IJOC2024+/Workshop_files/WS2_synth.py +++ b/examples/Example3/example_b.py @@ -133,5 +133,5 @@ cbar.set_ticks(batches) fig.suptitle("Variability in Simulation Time Increases \u2192", fontsize=ft) fig.tight_layout() -plt.savefig("Figure_WS_2.jpg", format="jpeg", bbox_inches="tight", dpi=500) +plt.savefig("ex3_b.png", format="jpeg", bbox_inches="tight", dpi=500) plt.show() diff --git a/examples/Example4/README.rst b/examples/Example4/README.rst new file mode 100644 index 0000000..6accbca --- /dev/null +++ b/examples/Example4/README.rst @@ -0,0 +1,54 @@ +Examples +~~~~~~~~ + +This example demonstrates how to use the active learning procedure from Sürer (2025), +Batch Sequential Experimental Design for Calibration of Stochastic Simulation Models, +for the stochastic models with high-dimensional outputs. + + +**Instructions for running the illustrative examples with the active learning procedure** + +To replicate the figures below, respectively: + +1) Go to the ``examples/Example4`` directory. + +2) Execute any of the following from the command line: + +.. code-block:: python + + python toy_example_exploit.py + python toy_example_explore.py + +Running this script should not take more than 5 min. See the figures (png files) saved under ``examples/Example4`` directory. + + +.. image:: toy1.png + :alt: Illustration of PUQ with the example + :align: center + :width: 600 + +Illustration with a simulation model. The red line shows the expected value of the simulation model (left +panel) or the likelihood (middle panel). The blue dashed line shows the prediction mean and the +shaded area illustrates one predictive standard deviation from the mean. Green dots indicate the +simulation data including five replicates of 20 uniformly spaced parameter values used to build +the emulator. The right panel demonstrates the true (black line) and estimated (blue dashed line) +intrinsic variance. + +.. image:: toy2.png + :alt: Illustration of PUQ with the example + :align: center + :width: 600 + +Allocation of new 100 replicates guided by the proposed acquisition function using the example above. +Star markers indicate the number of replicates on the existing 20 design points. The estimated intrinsic variance (blue +dashed line) and likelihood (red dotted line) are depicted for reference. + +.. image:: toy3.png + :alt: Illustration of PUQ with the example + :align: center + :width: 600 + +Allocation of b = 15 simulation evaluations guided by the IVAR criterion shown in +the first row. The emulator is constructed using simulation data including n0 = 6 unique parameters (black dots), +each replicated five times. The green star represents the acquired point. The second row shows the +true (red line) and estimated likelihood (blue dashed line). diff --git a/examples/Example4/example.py b/examples/Example4/example.py new file mode 100644 index 0000000..aec23be --- /dev/null +++ b/examples/Example4/example.py @@ -0,0 +1,79 @@ +import numpy as np +from PUQ.prior import prior_dist +from test_funcs import bimodal, banana, unimodal +from utilities import test_data_gen, twodpaper +from scipy.stats import qmc +from PUQ.designmethods.sequential_md_stochastic import sequential_design + + +# # # # # +batch = 8 +funcname = "unimodal" +smin, smax = 0, 1 +n0, rep0, rho = 15, 2, 1 / 2 +nmesh, maxiter = 50, 10 + +# Inputs to designer +desset = {"is_exploit": True, "is_explore": True, "nL": 200, "impute_str": "update"} + +if __name__ == "__main__": + + for s in np.arange(smin, smax): + + cls_func = eval(funcname)() + cls_func.realdata(seed=s) + + theta_test, p_test, f_test, Xpl, Ypl = test_data_gen(cls_func, nmesh) + test_data = {"theta": theta_test, "f": f_test, "p": p_test, "p_prior": 1} + + # heatmap(cls_func) + + # Set a uniform prior + prior_func = prior_dist(dist="uniform")( + a=cls_func.thetalimits[:, 0], b=cls_func.thetalimits[:, 1] + ) + + # Set random stream for initial design + persis_info = {"rand_stream": np.random.default_rng(s)} + + # Initial sample + sampling = qmc.LatinHypercube(d=cls_func.thetalimits.shape[0], seed=int(s)) + theta0 = sampling.random(n=n0) + theta0 = np.repeat(theta0, rep0, axis=0) + f0 = np.zeros((cls_func.d, n0 * rep0)) + for i in range(0, n0 * rep0): + f0[:, i] = cls_func.sim_f(theta0[i, :], persis_info=persis_info) + + base_args = { + "prior": prior_func, + "data_test": test_data, + "max_iter": maxiter, + "batch_size": batch, + "alloc_settings": { + "use_Ki": True, + "rho": rho, + "theta": None, + "a0": None, + "gen": False, + }, + "des_settings": desset, + } + + methods = ["ivar"] + + args_list = [] + for method in methods: + args_ = base_args.copy() + args_["alloc_settings"] = args_["alloc_settings"].copy() + args_["alloc_settings"]["method"] = method + args_list.append(args_) + + des_obj = sequential_design(cls_func) + des_obj.build_design(t0=theta0, f0=f0, af="seivar", args=args_list[0]) + + theta = des_obj["theta0"] + reps = des_obj["rep0"] + + twodpaper( + cls_func, Xpl, Ypl, p_test, theta, reps, thetainit=theta0, name="fig1" + ) diff --git a/examples/Example4/test_funcs.py b/examples/Example4/test_funcs.py new file mode 100644 index 0000000..ad0d341 --- /dev/null +++ b/examples/Example4/test_funcs.py @@ -0,0 +1,562 @@ +import numpy as np +import scipy + + +class unimodal: + def __init__(self): + self.data_name = "unimodal" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.obsvar = np.array([[0.1]], dtype="float64") + self.real_data = None + self.out = [("f", float)] + self.d = 1 + self.p = 2 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.5, 0.5]) + + def function(self, t1, t2): + t1 = -10 + t1 * 20 + t2 = -10 + t2 * 20 + f = 0.26 * (t1**2 + t2**2) - 0.48 * t1 * t2 + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V[0]), 1) + f += R + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + cov = np.array([[0.05, 0], [0, 0.05]]) + var = scipy.stats.multivariate_normal(mean=[0.85, 0.85], cov=cov) + return 2 * var.pdf(theta).reshape(self.d, theta.shape[0]) + + def realdata(self, seed): + + M = np.array( + self.function(self.theta_true[0], self.theta_true[1]), dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].normal(0, np.sqrt(self.obsvar[0]), size=1) + self.real_data = M + R + + +class bimodal: + def __init__(self): + self.data_name = "bimodal" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.obsvar = np.array([[0.5, 0], [0, 0.5]]) + self.theta_true = np.array([8 / 12, 8 / 12]) + self.real_data = None + self.out = [("f", float, (2,))] + self.p = 2 + self.d = 2 + self.x = np.arange(0, self.d)[:, None] + + def function(self, t1, t2): + t1 = 12 * t1 - 6 + t2 = 12 * t2 - 4 + f = np.array([np.sqrt(0.2) * (t2 - t1**2), np.sqrt(0.75) * (t2 - t1)]) + return f + + def sim_f(self, thetas, persis_info): + + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].multivariate_normal( + np.array([0, 0]), np.diag(V.flatten()), 1 + ) + f += R.flatten() + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + V = np.repeat(0.5 + (theta[:, 0] ** 2 + theta[:, 1] ** 2) * 2, self.d) + V = V.reshape(self.d, theta.shape[0]) + return V + + def realdata(self, seed): + + M = np.array( + [self.function(self.theta_true[0], self.theta_true[1])], dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].multivariate_normal( + mean=[0, 0], cov=self.obsvar, size=1 + ) + self.real_data = M + R + + +class banana: + def __init__(self): + self.data_name = "banana" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.obsvar = np.array([[0.03, 0], [0, 0.5]]) + self.theta_true = np.array([0.5, 0.75]) + self.real_data = None + self.out = [("f", float, (2,))] + self.p = 2 + self.d = 2 + self.x = np.arange(0, self.d)[:, None] + + def function(self, t1, t2): + t1 = 40 * t1 - 20 + t2 = 15 * t2 - 15 + f = np.array([0.03 * t1, t2 + 0.06 * t1**2]) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].multivariate_normal( + np.array([0, 0]), np.diag(V.flatten()), 1 + ) + f += R.flatten() + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + + f = self.function(theta[0, 0], theta[0, 1]) + if theta[0, 0] < 0.5: + noise1 = 0.01 * np.abs(f[0]) + noise2 = 0.01 * np.abs(f[1]) + else: + noise1 = 0.1 * np.abs(f[0]) + noise2 = 0.2 * np.abs(f[1]) + return np.array([noise1, noise2]).reshape(self.d, theta.shape[0]) + + def realdata(self, seed): + + M = np.array( + [self.function(self.theta_true[0], self.theta_true[1])], dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].multivariate_normal( + mean=[0, 0], cov=self.obsvar, size=1 + ) + self.real_data = M + R + + +class sinf: + def __init__(self): + self.data_name = "sinf" + self.thetalimits = np.array([[0, 1]]) + self.obsvar = np.array([[0.05]], dtype="float64") + self.theta_true = 0.5 + self.real_data = None + self.out = [("f", float)] + self.d = 1 + self.p = 1 + self.x = np.arange(0, self.d)[:, None] + + def function(self, theta1): + return np.sin(10 * theta1) + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0]) + var_noise = self.noise(np.array([thetas[0]])[None, :]) + noise = persis_info["rand_stream"].normal(0, np.sqrt(var_noise[0]), 1) + f += noise + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"], persis_info) + return H_o, persis_info + + def realdata(self, seed): + + mean = np.array([[self.function(self.theta_true)]], dtype="float64") + if seed is None: + self.real_data = mean + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + noise = persis_info["rand_stream"].normal( + 0, np.sqrt(self.obsvar[0]), size=1 + ) + self.real_data = mean + noise + + def noise(self, theta): + rx = (1.1 + np.sin(2 * np.pi * theta)) * 0.05 + return rx.reshape(self.d, theta.shape[0]) + + +class branin: + def __init__(self): + self.data_name = "branin" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.obsvar = np.array([[10]], dtype="float64") + self.real_data = None + self.out = [("f", float)] + self.d = 1 + self.p = 2 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.9613333333333334, 0.16466666666666668]) + + def function(self, t1, t2): + t1 = -5 + 15 * t1 + t2 = 15 * t2 + f = ( + (t2 - (5.1 / (4 * np.pi**2)) * (t1**2) + (5 / np.pi) * t1 - 6) ** 2 + + 10 * (1 - 1 / (8 * np.pi)) * np.cos(t1) + + 10 + ) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V[0]), 1) + f += R + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + return np.repeat(1, self.d * theta.shape[0]).reshape(self.d, theta.shape[0]) + + def realdata(self, seed): + + M = np.array( + self.function(self.theta_true[0], self.theta_true[1]), dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].normal(0, np.sqrt(self.obsvar[0]), size=1) + self.real_data = M + R + + +class himmelblau: + def __init__(self): + + self.data_name = "himmelblau" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.truelimits = np.array([[-5, 5], [-5, 5]]) + self.obsvar = np.array([[100]], dtype="float64") + self.real_data = None + self.out = [("f", float)] + self.p = 2 + self.d = 1 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.11085, 0.16]) + + def function(self, theta1, theta2): + + theta1 = self.truelimits[0][0] + theta1 * ( + self.truelimits[0][1] - self.truelimits[0][0] + ) + theta2 = self.truelimits[1][0] + theta2 * ( + self.truelimits[1][1] - self.truelimits[1][0] + ) + f = (theta1**2 + theta2 - 11) ** 2 + (theta1 + theta2**2 - 7) ** 2 + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V[0]), 1) + f += R + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + cov = np.array([[0.01, 0], [0, 0.01]]) + var = scipy.stats.multivariate_normal(mean=[0.5, 0.5], cov=cov) + return 10 + 5 * var.pdf(theta).reshape(self.d, theta.shape[0]) + + def realdata(self, seed): + + M = np.array( + self.function(self.theta_true[0], self.theta_true[1]), dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].normal(0, np.sqrt(self.obsvar[0]), size=1) + self.real_data = M + R + + +class holder: + def __init__(self): + + self.data_name = "holder" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.truelimits = np.array([[-10, 10], [-10, 10]]) + self.obsvar = np.array([[50]], dtype="float64") + self.real_data = None + self.out = [("f", float)] + self.p = 2 + self.d = 1 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.1, 0.01]) + + def function(self, theta1, theta2): + + theta1 = self.truelimits[0][0] + theta1 * ( + self.truelimits[0][1] - self.truelimits[0][0] + ) + theta2 = self.truelimits[1][0] + theta2 * ( + self.truelimits[1][1] - self.truelimits[1][0] + ) + f = -np.abs( + np.sin(theta1) + * np.cos(theta2) + * np.exp(np.abs(1 - (np.sqrt(theta1**2 + theta2**2) / np.pi))) + ) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V[0]), 1) + f += R + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + cov = np.array([[0.1, 0], [0, 0.1]]) + var = scipy.stats.multivariate_normal(mean=[0.5, 0.75], cov=cov) + return 1 + 2 * var.pdf(theta).reshape(self.d, theta.shape[0]) + + def realdata(self, seed): + + M = np.array( + self.function(self.theta_true[0], self.theta_true[1]), dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].normal(0, np.sqrt(self.obsvar[0]), size=1) + self.real_data = M + R + + +class easom: + def __init__(self): + + self.data_name = "easom" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.truelimits = np.array([[-10, 10], [-10, 10]]) + self.obsvar = np.array([[0.001]], dtype="float64") + self.real_data = None + self.out = [("f", float)] + self.p = 2 + self.d = 1 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.75, 0.75]) + + def function(self, theta1, theta2): + + theta1 = self.truelimits[0][0] + theta1 * ( + self.truelimits[0][1] - self.truelimits[0][0] + ) + theta2 = self.truelimits[1][0] + theta2 * ( + self.truelimits[1][1] - self.truelimits[1][0] + ) + f = ( + -np.cos(theta1) + * np.cos(theta2) + * np.exp(-((theta1 - np.pi) ** 2 + (theta2 - np.pi) ** 2)) + ) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V[0]), 1) + f += R + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + cov = np.array([[0.1, 0], [0, 0.1]]) + var = scipy.stats.multivariate_normal(mean=[0.5, 0.75], cov=cov) + return 1 + 2 * var.pdf(theta).reshape(self.d, theta.shape[0]) + + def realdata(self, seed): + + M = np.array( + self.function(self.theta_true[0], self.theta_true[1]), dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].normal(0, np.sqrt(self.obsvar[0]), size=1) + self.real_data = M + R + + +class ackley: + def __init__(self): + + self.data_name = "ackley" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.truelimits = np.array([[-5, 5], [-5, 5]]) + self.obsvar = np.array([[10]], dtype="float64") + self.real_data = None + self.out = [("f", float)] + self.p = 2 + self.d = 1 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.5, 0.5]) + + def function(self, theta1, theta2): + + theta1 = self.truelimits[0][0] + theta1 * ( + self.truelimits[0][1] - self.truelimits[0][0] + ) + theta2 = self.truelimits[1][0] + theta2 * ( + self.truelimits[1][1] - self.truelimits[1][0] + ) + f = ( + -20.0 * np.exp(-0.2 * np.sqrt(0.5 * (theta1**2 + theta2**2))) + - np.exp(0.5 * (np.cos(2 * np.pi * theta1) + np.cos(2 * np.pi * theta2))) + + np.e + + 20 + ) + + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V[0]), 1) + f += R + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + cov = np.array([[0.1, 0], [0, 0.1]]) + var = scipy.stats.multivariate_normal(mean=[0.5, 0.85], cov=cov) + return 1 + 2 * var.pdf(theta).reshape(self.d, theta.shape[0]) + + def realdata(self, seed): + + M = np.array( + self.function(self.theta_true[0], self.theta_true[1]), dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].normal(0, np.sqrt(self.obsvar[0]), size=1) + self.real_data = M + R + + +class sphere: + def __init__(self): + + self.data_name = "sphere" + self.thetalimits = np.array([[0, 1], [0, 1]]) + self.truelimits = np.array([[-5, 5], [-5, 5]]) + self.obsvar = np.array([[1]], dtype="float64") + self.real_data = None + self.out = [("f", float)] + self.p = 2 + self.d = 1 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.5, 0.5]) + + def function(self, theta1, theta2): + theta1 = self.truelimits[0][0] + theta1 * ( + self.truelimits[0][1] - self.truelimits[0][0] + ) + theta2 = self.truelimits[1][0] + theta2 * ( + self.truelimits[1][1] - self.truelimits[1][0] + ) + f = theta1**2 + theta2**2 + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + V = self.noise(np.array([thetas[0], thetas[1]])[None, :]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V[0]), 1) + f += R + return f + + def sim(self, H, persis_info, sim_specs, libE_info): + function = sim_specs["user"]["sim_f"] + H_o = np.zeros(1, dtype=sim_specs["out"]) + H_o["f"] = function(H["thetas"][0], persis_info) + + return H_o, persis_info + + def noise(self, theta): + cov = np.array([[0.1, 0], [0, 0.1]]) + var = scipy.stats.multivariate_normal(mean=[0.5, 0.5], cov=cov) + return 1 + 1 * var.pdf(theta).reshape(self.d, theta.shape[0]) + + def realdata(self, seed): + + M = np.array( + self.function(self.theta_true[0], self.theta_true[1]), dtype="float64" + ) + if seed is None: + self.real_data = M + else: + persis_info = {"rand_stream": np.random.default_rng(seed)} + R = persis_info["rand_stream"].normal(0, np.sqrt(self.obsvar[0]), size=1) + self.real_data = M + R diff --git a/examples/Example4/toy1.png b/examples/Example4/toy1.png new file mode 100644 index 0000000..d655aa3 Binary files /dev/null and b/examples/Example4/toy1.png differ diff --git a/examples/Example4/toy2.png b/examples/Example4/toy2.png new file mode 100644 index 0000000..08d16bf Binary files /dev/null and b/examples/Example4/toy2.png differ diff --git a/examples/Example4/toy3.png b/examples/Example4/toy3.png new file mode 100644 index 0000000..6ba38df Binary files /dev/null and b/examples/Example4/toy3.png differ diff --git a/examples/Example4/toy_example_exploit.py b/examples/Example4/toy_example_exploit.py new file mode 100644 index 0000000..fbb057e --- /dev/null +++ b/examples/Example4/toy_example_exploit.py @@ -0,0 +1,82 @@ +import numpy as np +from PUQ.prior import prior_dist +from test_funcs import sinf +from utilities import test_data_gen_1d, Figure1, Figure2 +import matplotlib.pyplot as plt +from PUQ.designmethods.sequential_md_stochastic import sequential_design + + +batch, example, smin, smax = 100, "sinf", 13, 14 +n0, rep0, maxiter, rho = 20, 5, 1, 1 / 2 + +# Inputs to designer +desset = {"is_exploit": True, "is_explore": False, "nL": 200, "impute_str": "update"} +if __name__ == "__main__": + for s in np.arange(smin, smax): + + cls_func = eval(example)() + cls_func.realdata(s) + + theta_test, p_test, f_test = test_data_gen_1d(cls_func, 100) + test_data = {"theta": theta_test, "f": f_test, "p": p_test, "p_prior": 1} + + # Set a uniform prior + prior_func = prior_dist(dist="uniform")( + a=cls_func.thetalimits[:, 0], b=cls_func.thetalimits[:, 1] + ) + + # Set random stream for initial design + persis_info = {"rand_stream": np.random.default_rng(s)} + + theta = np.linspace(cls_func.thetalimits[0][0], cls_func.thetalimits[0][1], n0)[ + :, None + ] + theta = np.repeat(theta, rep0, axis=0) + f = np.zeros((cls_func.d, rep0 * n0)) + for i in range(0, rep0 * n0): + f[:, i] = cls_func.sim_f(theta[i, :], persis_info=persis_info) + + theta_test, nugs, phat = Figure1( + f, + theta, + cls_func.x, + cls_func.obsvar, + cls_func.real_data, + theta_test, + f_test, + p_test, + cls_func, + ) + + base_args = { + "prior": prior_func, + "data_test": test_data, + "max_iter": maxiter, + "batch_size": batch, + "alloc_settings": { + "use_Ki": True, + "rho": rho, + "theta": None, + "a0": None, + "gen": False, + }, + "des_settings": desset, + } + + methods = ["ivar"] + + args_list = [] + for method in methods: + args_ = base_args.copy() + args_["alloc_settings"] = args_["alloc_settings"].copy() + args_["alloc_settings"]["method"] = method + args_list.append(args_) + + des_obj = sequential_design(cls_func) + des_obj.build_design(t0=theta, f0=f, af="seivar", args=args_list[0]) + + fig, ax = plt.subplots(1, 1, figsize=(7, 3.5)) + fig.subplots_adjust(wspace=0.6) + Figure2(des_obj, theta_test, nugs, phat, "ivar", ax) + plt.savefig("toy2.png", format="jpeg", bbox_inches="tight", dpi=1000) + plt.show() diff --git a/examples/Example4/toy_example_explore.py b/examples/Example4/toy_example_explore.py new file mode 100644 index 0000000..700f2e8 --- /dev/null +++ b/examples/Example4/toy_example_explore.py @@ -0,0 +1,171 @@ +import numpy as np +from PUQ.prior import prior_dist +from test_funcs import sinf +from utilities import test_data_gen_1d +import matplotlib.pyplot as plt +import scipy.stats as sps +from PUQ.designmethods.gen_funcs.batch_acquisition_funcs_support import ( + build_emulator, + multiple_determinants, + multiple_pdfs, +) +from PUQ.designmethods.gen_funcs.acquisition_md_stochastic import get_pred + +funcname, smin, smax = "sinf", 3, 4 +n0, rep0, nmesh, batch, reps = 6, 10, 50, 15, 5 +bnew = int(batch / reps) + +if __name__ == "__main__": + for s in np.arange(smin, smax): + cls_func = eval(funcname)() + cls_func.realdata(seed=s) + + theta_test, p_test, f_test = test_data_gen_1d(cls_func, 100) + test_data = {"theta": theta_test, "f": f_test, "p": p_test, "p_prior": 1} + + # Set a uniform prior + prior_func = prior_dist(dist="uniform")( + a=cls_func.thetalimits[:, 0], b=cls_func.thetalimits[:, 1] + ) + persis_info = {"rand_stream": np.random.default_rng(12345)} + + # Initial sample + theta0u = np.linspace( + cls_func.thetalimits[0][0], cls_func.thetalimits[0][1], n0 + )[:, None] + f0mean = np.zeros(len(theta0u)) + p0 = np.zeros(len(theta0u)) + theta0 = np.repeat(theta0u, rep0, axis=0) + f0 = np.zeros(len(theta0)) + for tid, t in enumerate(theta0): + f0[tid] = cls_func.sim_f(t, persis_info=persis_info)[0] + + for tid, t in enumerate(theta0u): + f0mean[tid] = cls_func.function(t)[0] + rnd = sps.norm(loc=f0mean[tid], scale=np.sqrt(cls_func.obsvar)) + p0[tid] = rnd.pdf(cls_func.real_data)[0, 0] + + n_x, p, x = cls_func.x.shape[0], theta_test.shape[1], cls_func.x + obs, obsvar = cls_func.real_data, cls_func.obsvar + theta_acq, n_acq = None, None + obsvar3d = cls_func.obsvar.reshape(1, n_x, n_x) + is_cov = False + + # Create a candidate list + nL = 500 + nm = theta_test.shape[0] + + fig, ax = plt.subplots(2, 3, figsize=(15, 7), constrained_layout=True) + + emu = build_emulator(x=x, theta=theta0, f=f0[None, :], pcset=None) + for i in range(3): + ft = 18 + cL = np.linspace( + cls_func.thetalimits[0][0], cls_func.thetalimits[0][1], nL + )[:, None] + + mu, S, cov, cvar = get_pred( + cL=cL, emu=emu, x=x, ttest=theta_test, reps=reps + ) + + # plt.plot(theta_test, mu) + # plt.show() + + V1 = S + obsvar3d + V2 = S + 0.5 * obsvar3d + + # G = emu._info["G"] + # B = emu._info["B"] + # GB = G @ B + # BTG = B.T @ G + + # d = G.shape[0] + # q = B.shape[1] + d, q = 1, 1 + # tau = np.zeros((nm, q, q, nL)) + phi = np.zeros((nm, d, d, nL)) + + coef = 1 / ((2**d) * (np.sqrt(np.pi) ** d)) + + for j in range(0, q): + phi[:, j, j, :] = cov[j, :, :] ** 2 / cvar[j, :] + + # for k in range(0, nL): + # phi[:, :, :, k] = GB @ tau[:, :, :, k] @ BTG + + vals = [] + for k in range(0, nL): + # C1: nm x d x d + # C2: nm x d x d + phic = phi[:, x, x.T, k] + C1 = (V1 + phic) * 0.5 + C2 = V1 - phic + + rpdf = multiple_pdfs(obs, mu, C1) + dets = multiple_determinants(C2) + part2 = rpdf / np.sqrt(dets) + + rpdf2 = multiple_pdfs(obs, mu, V2) + denum2 = obsvar + + cval = coef * (np.sum(rpdf2 / np.sqrt(denum2)) - np.sum(part2)) + + vals.append(cval) + + idc = np.argmin(vals) + minacq = np.min(vals) + cu = cL[idc, :].reshape((1, p)) + + ax[0, i].plot(cL, vals, color="blue") + ax[0, i].set_xlabel(r"$\theta$", fontsize=ft) + ax[0, i].set_ylabel("IVAR", fontsize=ft) + ax[0, i].tick_params(axis="both", labelsize=ft) + ax[0, i].scatter(cu, minacq, color="green", marker="*", s=200) + + fc = np.zeros(len(cu)) + pc = np.zeros(len(cu)) + for tid, t in enumerate(cu): + fc[tid] = cls_func.function(t) + rnd = sps.norm(loc=fc[tid], scale=np.sqrt(cls_func.obsvar)) + pc[tid] = rnd.pdf(cls_func.real_data) + + phat = np.zeros(theta_test.shape[0]) + phatvar = np.zeros(theta_test.shape[0]) + pvar1 = np.zeros(theta_test.shape[0]) + for tid in range(0, len(theta_test)): + rnd = sps.norm(loc=mu[tid, 0], scale=np.sqrt(obsvar + S[tid, 0, 0])) + phat[tid] = rnd.pdf(obs) + rnd = sps.norm( + loc=mu[tid, 0], scale=np.sqrt(0.5 * obsvar + S[tid, 0, 0]) + ) + pvar1[tid] = rnd.pdf(obs) + phatvar[tid] = (1 / (2 * np.sqrt(np.pi) * np.sqrt(obsvar))) * pvar1[ + tid + ] - phat[tid] ** 2 + + ax[1, i].plot(theta_test, p_test, color="red") + ax[1, i].plot( + theta_test, phat, color="blue", linestyle="dashed", linewidth=2.5 + ) + ax[1, i].fill_between( + theta_test.flatten(), + (phat - np.sqrt(phatvar)).flatten(), + (phat + np.sqrt(phatvar)).flatten(), + color="blue", + alpha=0.1, + ) + ax[1, i].set_xlabel(r"$\theta$", fontsize=ft) + ax[1, i].set_ylabel(r"$p(y|\theta)$", fontsize=ft) + ax[1, i].tick_params(axis="both", labelsize=ft) + ax[1, i].scatter(theta0u, p0, color="black", s=100) + ax[1, i].scatter(cu, pc, color="green", marker="*", s=200) + + theta0u = np.concatenate([theta0u, cu], axis=0) + p0 = np.concatenate([p0, pc.flatten()]) + + print("Adding point:", np.round(cu, 2)) + + X0new = np.repeat(cu, rep0)[:, None] + emu.update(x=X0new) + plt.savefig("toy3.png", format="jpeg", bbox_inches="tight", dpi=1000) + plt.show() diff --git a/examples/Example4/utilities.py b/examples/Example4/utilities.py new file mode 100644 index 0000000..bb54107 --- /dev/null +++ b/examples/Example4/utilities.py @@ -0,0 +1,814 @@ +import numpy as np +import matplotlib.pyplot as plt +import scipy.stats as sps + + +def twoD(designobj, Xpl, Ypl, p_test, nmesh): + theta0 = designobj._info["theta0"] + reps0 = designobj._info["reps0"] + + fig, ax = plt.subplots(figsize=(5, 5)) + cp = ax.contour(Xpl, Ypl, p_test.reshape(nmesh, nmesh), 20, cmap="RdGy") + for label, x_count, y_count in zip(reps0, theta0[:, 0], theta0[:, 1]): + if label == 2: + plt.annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=12, + color="cyan", + weight="bold", + ) + else: + plt.annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=12, + color="blue", + weight="bold", + ) + ax.set_xlabel(r"$\theta_1$", fontsize=16) + ax.set_ylabel(r"$\theta_2$", fontsize=16) + ax.tick_params(axis="both", labelsize=16) + plt.show() + + +def test_data_gen(cls_func, nmesh): + xpl = np.linspace(cls_func.thetalimits[0][0], cls_func.thetalimits[0][1], nmesh) + ypl = np.linspace(cls_func.thetalimits[1][0], cls_func.thetalimits[1][1], nmesh) + Xpl, Ypl = np.meshgrid(xpl, ypl) + theta_test = np.vstack([Xpl.ravel(), Ypl.ravel()]).T + if cls_func.data_name in [ + "unimodal", + "branin", + "himmelblau", + "holder", + "easom", + "ackley", + "sphere", + ]: + f_test = np.zeros(theta_test.shape[0]) + else: + f_test = np.zeros((theta_test.shape[0], 2)) + p_test = np.zeros(theta_test.shape[0]) + for tid in range(0, len(theta_test)): + if cls_func.data_name in [ + "unimodal", + "branin", + "himmelblau", + "holder", + "easom", + "ackley", + "sphere", + ]: + f_test[tid] = cls_func.function(theta_test[tid, 0], theta_test[tid, 1]) + rnd = sps.norm(loc=f_test[tid], scale=np.sqrt(cls_func.obsvar)) + else: + f_test[tid, :] = cls_func.function(theta_test[tid, 0], theta_test[tid, 1]) + rnd = sps.multivariate_normal(mean=f_test[tid, :], cov=(cls_func.obsvar)) + p_test[tid] = rnd.pdf(cls_func.real_data) + + return theta_test, p_test, f_test, Xpl, Ypl + + +def test_data_gen_1d(cls_func, nmesh): + + # Create test data + theta_test = np.linspace( + cls_func.thetalimits[0][0], cls_func.thetalimits[0][1], nmesh + )[:, None] + f_test, p_test = np.zeros(theta_test.shape[0]), np.zeros(theta_test.shape[0]) + for tid in range(0, len(theta_test)): + f_test[tid] = cls_func.function(theta_test[tid, 0]) + rnd = sps.norm(loc=f_test[tid], scale=np.sqrt(cls_func.obsvar)) + p_test[tid] = rnd.pdf(cls_func.real_data) + + return theta_test, p_test, f_test + + +def oned(seqobject, x, obsvar, real_data, theta_test, f_test, p_test, cls_func, ninit): + from PUQ.surrogate import emulator + from scipy.stats import norm + import matplotlib.pyplot as plt + + pc_settings = {"standardize": True, "latent": True} + + theta = seqobject._info["theta"][0:ninit, :] + f = seqobject._info["f"][:, 0:ninit] + + theta_all = seqobject._info["theta"] + f_all = seqobject._info["f"] + + theta0 = seqobject._info["theta0"] + reps0 = seqobject._info["reps0"] + + emu = emulator( + x=x, + theta=theta, + f=f, + method="pcHetGP", + args={ + "lower": None, + "upper": None, + "noiseControl": { + "k_theta_g_bounds": (1, 100), + "g_max": 1e2, + "g_bounds": (1e-6, 1), + }, + "init": {}, + "known": {}, + "settings": { + "linkThetas": "joint", + "logN": True, + "initStrategy": "residuals", + "checkHom": True, + "penalty": True, + "trace": 0, + "return.matrices": True, + "return.hom": False, + "factr": 1e9, + }, + "pc_settings": pc_settings, + }, + ) + + emupred = emu.predict(x=x, theta=theta_test) + + mean = emupred.mean() + var = emupred.var() + var_noisy = emupred._info["var_noisy"] + + # Probability corresponding to the quantile (e.g., 0.025 for the lower bound) + quantile = 0.025 + lower_bound = norm.ppf(quantile, loc=mean, scale=np.sqrt(var)) + quantile = 0.975 + upper_bound = norm.ppf(quantile, loc=mean, scale=np.sqrt(var)) + # Probability corresponding to the quantile (e.g., 0.025 for the lower bound) + quantile = 0.025 + lower_bound_nug = norm.ppf(quantile, loc=mean, scale=np.sqrt(var_noisy)) + quantile = 0.975 + upper_bound_nug = norm.ppf(quantile, loc=mean, scale=np.sqrt(var_noisy)) + + ft = 20 + fig, ax = plt.subplots() + ax.plot(theta_test, f_test, color="red") + ax.plot( + theta_test.flatten(), + mean.flatten(), + linestyle="dashed", + color="blue", + linewidth=2.5, + ) + plt.fill_between( + theta_test.flatten(), + mean.flatten() - np.sqrt(var.flatten()), + mean.flatten() + np.sqrt(var.flatten()), + # lower_bound.flatten(), + # upper_bound.flatten(), + color="blue", + alpha=0.3, + linestyle="dotted", + ) + + # plt.fill_between( + # theta_test.flatten(), + # lower_bound_nug.flatten(), + # upper_bound_nug.flatten(), + # color='blue', + # alpha=0.1, + # linestyle="dotted", + # ) + ax.scatter( + theta.flatten(), f.flatten(), s=60, facecolors="none", edgecolors="green" + ) + ax.set_xlabel(r"$\theta$", fontsize=ft) + ax.set_ylabel(r"$M(\theta)$", fontsize=ft) + ax.tick_params(axis="both", labelsize=ft) + plt.show() + + phat = np.zeros(theta_test.shape[0]) + phatvar = np.zeros(theta_test.shape[0]) + pvar1 = np.zeros(theta_test.shape[0]) + for tid in range(0, len(theta_test)): + rnd = sps.norm(loc=mean[0, tid], scale=np.sqrt(obsvar + var[0, tid])) + phat[tid] = rnd.pdf(real_data) + rnd = sps.norm(loc=mean[0, tid], scale=np.sqrt(0.5 * obsvar + var[0, tid])) + pvar1[tid] = rnd.pdf(real_data) + phatvar[tid] = (1 / (2 * np.sqrt(np.pi) * np.sqrt(obsvar))) * pvar1[tid] - phat[ + tid + ] ** 2 + + fig, ax = plt.subplots() + ax.plot(theta_test, p_test, color="red") + ax.plot(theta_test, phat, color="blue", linestyle="dashed", linewidth=2.5) + plt.fill_between( + theta_test.flatten(), + (phat - np.sqrt(phatvar)).flatten(), + (phat + np.sqrt(phatvar)).flatten(), + color="blue", + alpha=0.1, + ) + ax.set_xlabel(r"$\theta$", fontsize=ft) + ax.set_ylabel(r"$p(y|\theta)$", fontsize=ft) + ax.tick_params(axis="both", labelsize=ft) + plt.show() + + p_alloc = np.zeros(theta0.shape[0]) + f_alloc = np.zeros((1, theta0.shape[0])) + for tid in range(0, len(theta0)): + f_alloc[0, tid] = cls_func.function(theta0[tid]) + rnd = sps.norm(loc=f_alloc[0, tid], scale=np.sqrt(cls_func.obsvar)) + p_alloc[tid] = rnd.pdf(cls_func.real_data) + + fig, ax = plt.subplots(figsize=(10, 6)) + ax.plot(theta_test, p_test, color="red", linewidth=2.5) + for label, x_count, pval in zip(reps0, theta0, p_alloc): + plt.annotate( + label, + xy=(x_count, pval), + xytext=(0, 0), + textcoords="offset points", + color="blue", + size=20, + ) + ax.set_xlabel(r"$\theta$", fontsize=ft) + ax.set_ylabel(r"$p(y|\theta)$", fontsize=ft) + ax.tick_params(axis="both", labelsize=ft) + plt.show() + + fig, ax = plt.subplots() + ax.scatter(theta0.flatten(), reps0.flatten(), color="blue", marker="*", s=55) + ax.set_xlabel(r"$\theta$", fontsize=ft) + ax.set_ylabel(r"a", fontsize=ft) + ax.tick_params(axis="both", labelsize=ft) + plt.show() + + fig, ax = plt.subplots() + ax.plot( + theta_test.flatten(), + emupred._info["nugs"].flatten(), + color="blue", + linestyle="dashed", + linewidth=2.5, + ) + ax.plot(theta_test.flatten(), cls_func.noise(theta_test).flatten(), color="black") + ax.set_xlabel(r"$\theta$", fontsize=ft) + ax.set_ylabel(r"$\mathbb{V}[\nu]$", fontsize=ft) + ax.tick_params(axis="both", labelsize=ft) + plt.show() + + ft = 15 + + def make_patch_spines_invisible(ax): + ax.set_frame_on(True) + ax.patch.set_visible(False) + for sp in ax.spines.values(): + sp.set_visible(False) + + fig, host = plt.subplots(figsize=(8, 4)) + fig.subplots_adjust(right=0.75) + + par1 = host.twinx() + par2 = host.twinx() + + # Offset the right spine of par2. The ticks and label have already been + # placed on the right by twinx above. + par2.spines["right"].set_position(("axes", 1.2)) + # Having been created by twinx, par2 has its frame off, so the line of its + # detached spine is invisible. First, activate the frame but make the patch + # and spines invisible. + make_patch_spines_invisible(par2) + # Second, show the right spine. + par2.spines["right"].set_visible(True) + + p1 = host.scatter( + theta0.flatten(), + reps0.flatten(), + color="black", + marker="*", + s=55, + label=r"$a_i$", + ) + (p2,) = par1.plot( + theta_test.flatten(), + emupred._info["nugs"].flatten(), + "b", + linestyle="dashed", + label="Variance", + linewidth=2.5, + ) + (p3,) = par2.plot( + theta_test, phat, "r", linestyle="dotted", label="Likelihood", linewidth=2.5 + ) + + host.set_xlim(-0.1, 1.1) + host.set_ylim(0.9 * np.min(reps0.flatten()), 1.1 * np.max(reps0.flatten())) + par1.set_ylim( + 0.9 * np.min(emupred._info["nugs"].flatten()), + 1.1 * np.max(emupred._info["nugs"].flatten()), + ) + par2.set_ylim(-0.01, 1.1 * np.max(phat)) + + host.set_xlabel(r"$\theta$", fontsize=ft) + host.set_ylabel(r"$a_i$", fontsize=ft) + par1.set_ylabel("Variance", fontsize=ft) + par2.set_ylabel("Likelihood", fontsize=ft) + host.tick_params(axis="both", labelsize=ft) + par1.tick_params(axis="both", labelsize=ft) + par2.tick_params(axis="both", labelsize=ft) + lines = [p1, p2, p3] + + host.legend( + lines, + [l.get_label() for l in lines], + loc="center", + bbox_to_anchor=(0.5, -0.3), + ncol=3, + prop={"size": ft}, + ) + + plt.show() + + +def twodpaper(cls_func, Xpl, Ypl, p_test, theta0, reps0, thetainit=None, name=None): + from matplotlib.ticker import MaxNLocator + from matplotlib.colors import ListedColormap + + yellow_colors = [ + (1, 1, 1), + (1, 1, 0.8), # light yellow + (1, 1, 0.6), + (1, 1, 0.4), + (1, 1, 0.2), + (1, 1, 0), # yellow + (1, 0.9, 0), # dark yellow + (1, 0.8, 0), # yellow-orange + (1, 0.6, 0), # orange + (1, 0.4, 0), # dark orange + (1, 0.2, 0), # very dark orange + ] + yellow_cmap = ListedColormap(yellow_colors, name="yellow") + + if cls_func.data_name in ["unimodal", "branin"]: + nmesh = len(Xpl) + P = np.zeros((nmesh, nmesh)) + for i in range(nmesh): + for j in range(nmesh): + P[i, j] = cls_func.noise( + np.array([Xpl[i, j], Ypl[i, j]])[None, :] + ).flatten() + + Pvar = P + else: + nmesh = len(Xpl) + P = np.zeros((nmesh, nmesh, 2)) + for i in range(nmesh): + for j in range(nmesh): + P[i, j, :] = cls_func.noise( + np.array([Xpl[i, j], Ypl[i, j]])[None, :] + ).flatten() + Pvar = np.sum(P, axis=2) + + fig, ax = plt.subplots() + cs = ax.contourf(Xpl, Ypl, Pvar, cmap=yellow_cmap, alpha=0.75) + # cbar = fig.colorbar(cs, pad=0.1) + cp = ax.contour(Xpl, Ypl, p_test.reshape(nmesh, nmesh), 20, cmap="coolwarm") + + if thetainit is None: + for label, x_count, y_count in zip(reps0, theta0[:, 0], theta0[:, 1]): + if label <= 2: + plt.annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=12, + color="cyan", + ) + else: + plt.annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=12, + color="black", + ) + else: + for label, x_count, y_count in zip(reps0, theta0[:, 0], theta0[:, 1]): + if np.array([x_count, y_count]) in thetainit: + plt.annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=12, + color="cyan", + ) + else: + plt.annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=12, + color="black", + ) + + ax.set_xticks([0, 0.5, 1]) # Custom tick locations for x-axis + ax.set_yticks([0, 0.5, 1]) # Custom tick locations for y-axis + ax.set_xlabel(r"$\theta_1$", fontsize=16) + ax.set_ylabel(r"$\theta_2$", fontsize=16) + ax.tick_params(axis="both", labelsize=16) + plt.savefig(name, bbox_inches="tight") + plt.show() + + +def twodpaperrev(cls_func, Xpl, Ypl, p_test, dictl, thetainit=None, name=None): + from matplotlib.ticker import MaxNLocator + from matplotlib.colors import ListedColormap + + yellow_colors = [ + (1, 1, 1), + (1, 1, 0.8), # light yellow + (1, 1, 0.6), + (1, 1, 0.4), + (1, 1, 0.2), + (1, 1, 0), # yellow + (1, 0.9, 0), # dark yellow + (1, 0.8, 0), # yellow-orange + (1, 0.6, 0), # orange + (1, 0.4, 0), # dark orange + (1, 0.2, 0), # very dark orange + ] + yellow_cmap = ListedColormap(yellow_colors, name="yellow") + + if cls_func.data_name in ["unimodal", "branin"]: + nmesh = len(Xpl) + P = np.zeros((nmesh, nmesh)) + for i in range(nmesh): + for j in range(nmesh): + P[i, j] = cls_func.noise( + np.array([Xpl[i, j], Ypl[i, j]])[None, :] + ).flatten() + + Pvar = P + else: + nmesh = len(Xpl) + P = np.zeros((nmesh, nmesh, 2)) + for i in range(nmesh): + for j in range(nmesh): + P[i, j, :] = cls_func.noise( + np.array([Xpl[i, j], Ypl[i, j]])[None, :] + ).flatten() + Pvar = np.sum(P, axis=2) + + fig, ax = plt.subplots(1, 3, figsize=(15, 4.5), constrained_layout=True) + + for i in range(0, 3): + if i == 0: + for el in dictl: + if el["method"] == "ivar": + reps0 = el["reps0"] + theta0 = el["theta0"] + elif i == 1: + for el in dictl: + if el["method"] == "var": + reps0 = el["reps0"] + theta0 = el["theta0"] + elif i == 2: + for el in dictl: + if el["method"] == "imse": + reps0 = el["reps0"] + theta0 = el["theta0"] + + cs = ax[i].contourf(Xpl, Ypl, Pvar, cmap=yellow_cmap, alpha=0.75) + if i == 2: + cbar = fig.colorbar(cs, ax=ax[i], pad=0.1) + cp = ax[i].contour(Xpl, Ypl, p_test.reshape(nmesh, nmesh), 20, cmap="coolwarm") + ft = 14 + if thetainit is None: + for label, x_count, y_count in zip(reps0, theta0[:, 0], theta0[:, 1]): + if label <= 2: + ax[i].annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=ft, + color="cyan", + ) + else: + ax[i].annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=ft, + color="black", + ) + else: + for label, x_count, y_count in zip(reps0, theta0[:, 0], theta0[:, 1]): + if np.array([x_count, y_count]) in thetainit: + ax[i].annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=ft, + color="cyan", + ) + else: + ax[i].annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=ft, + color="black", + ) + + if i == 0: + ax[i].set_yticks([0, 0.5, 1]) # Custom tick locations for x-axis + ax[i].set_ylabel(r"$\theta_2$", fontsize=16) + else: + ax[i].set_yticks([]) + ax[i].set_xticks([0, 0.5, 1]) + + ax[i].set_xlabel(r"$\theta_1$", fontsize=16) + + ax[i].tick_params(axis="both", labelsize=16) + + plt.savefig(name, bbox_inches="tight") + plt.show() + + +def heatmap(cls_func): + + from matplotlib.colors import ListedColormap + + yellow_colors = [ + (1, 1, 1), + (1, 1, 0.8), # light yellow + (1, 1, 0.6), + (1, 1, 0.4), + (1, 1, 0.2), + (1, 1, 0), # yellow + (1, 0.9, 0), # dark yellow + (1, 0.8, 0), # yellow-orange + (1, 0.6, 0), # orange + (1, 0.4, 0), # dark orange + (1, 0.2, 0), # very dark orange + ] + yellow_cmap = ListedColormap(yellow_colors, name="yellow") + + if cls_func.data_name in ["unimodal", "branin"]: + nmesh = 50 + a = np.arange(nmesh + 1) / nmesh + b = np.arange(nmesh + 1) / nmesh + X, Y = np.meshgrid(a, b) + Z = np.zeros((nmesh + 1, nmesh + 1)) + P = np.zeros((nmesh + 1, nmesh + 1)) + for i in range(nmesh + 1): + for j in range(nmesh + 1): + Z[i, j] = cls_func.function(X[i, j], Y[i, j]) + P[i, j] = cls_func.noise( + np.array([X[i, j], Y[i, j]])[None, :] + ).flatten() + + fig, ax = plt.subplots() + cs = ax.contourf(X, Y, P, cmap=yellow_cmap, alpha=0.75) + cbar = fig.colorbar(cs) + CS = ax.contour(X, Y, Z, colors="black") + ax.clabel(CS, inline=True, fontsize=10) + ax.set_xlabel(r"$\theta_1$", fontsize=16) + ax.set_ylabel(r"$\theta_2$", fontsize=16) + ax.tick_params(axis="both", labelsize=16) + plt.show() + else: + nmesh = 50 + a = np.arange(nmesh + 1) / nmesh + b = np.arange(nmesh + 1) / nmesh + X, Y = np.meshgrid(a, b) + Z = np.zeros((nmesh + 1, nmesh + 1, 2)) + P = np.zeros((nmesh + 1, nmesh + 1, 2)) + + for i in range(nmesh + 1): + for j in range(nmesh + 1): + Z[i, j, :] = cls_func.function(X[i, j], Y[i, j]) + P[i, j, :] = cls_func.noise( + np.array([X[i, j], Y[i, j]])[None, :] + ).flatten() + + fig, ax = plt.subplots() + cs = ax.contourf(X, Y, P[:, :, 0], cmap=yellow_cmap, alpha=0.75) + cbar = fig.colorbar(cs) + CS = ax.contour(X, Y, Z[:, :, 0], colors="black") + ax.clabel(CS, inline=True, fontsize=10) + ax.set_xlabel(r"$\theta_1$", fontsize=16) + ax.set_ylabel(r"$\theta_2$", fontsize=16) + ax.tick_params(axis="both", labelsize=16) + plt.show() + + fig, ax = plt.subplots() + cs = ax.contourf(X, Y, P[:, :, 1], cmap=yellow_cmap, alpha=0.75) + cbar = fig.colorbar(cs) + CS = ax.contour(X, Y, Z[:, :, 1], colors="black") + ax.clabel(CS, inline=True, fontsize=10) + ax.set_xlabel(r"$\theta_1$", fontsize=16) + ax.set_ylabel(r"$\theta_2$", fontsize=16) + ax.tick_params(axis="both", labelsize=16) + plt.show() + + +def Figure1(f, theta, x, obsvar, real_data, theta_test, f_test, p_test, cls_func): + from PUQ.surrogate import emulator + from scipy.stats import norm + import matplotlib.pyplot as plt + + pc_settings = {"standardize": True, "latent": False} + d = f.shape[0] + md = np.arange(d).reshape(d, 1) + emu = emulator( + x=theta, + theta=md, + f=f, + method="multihetGP", + args={ + "lower": None, + "upper": None, + "noiseControl": { + "k_theta_g_bounds": (1, 100), + "g_max": 1e2, + "g_bounds": (1e-6, 1), + }, + "init": {}, + "known": {}, + "settings": { + "linkThetas": "joint", + "logN": True, + "initStrategy": "residuals", + "checkHom": True, + "penalty": True, + "trace": 0, + "return.matrices": True, + "return.hom": False, + "factr": 1e9, + }, + }, + ) + + emupred = emu.predict(x=theta_test) + + mean = emupred.mean() + var = emupred.var() + var_noisy = emupred._info["var"] + emupred._info["nugs"] + + # Probability corresponding to the quantile (e.g., 0.025 for the lower bound) + quantile = 0.025 + lower_bound = norm.ppf(quantile, loc=mean, scale=np.sqrt(var)) + quantile = 0.975 + upper_bound = norm.ppf(quantile, loc=mean, scale=np.sqrt(var)) + # Probability corresponding to the quantile (e.g., 0.025 for the lower bound) + quantile = 0.025 + lower_bound_nug = norm.ppf(quantile, loc=mean, scale=np.sqrt(var_noisy)) + quantile = 0.975 + upper_bound_nug = norm.ppf(quantile, loc=mean, scale=np.sqrt(var_noisy)) + + fig, ax = plt.subplots(1, 3, figsize=(15, 3.5), constrained_layout=True) + ft = 18 + ax[0].plot(theta_test, f_test, color="red") + ax[0].plot( + theta_test.flatten(), + mean.flatten(), + linestyle="dashed", + color="blue", + linewidth=2.5, + ) + ax[0].fill_between( + theta_test.flatten(), + mean.flatten() - np.sqrt(var.flatten()), + mean.flatten() + np.sqrt(var.flatten()), + # lower_bound.flatten(), + # upper_bound.flatten(), + color="blue", + alpha=0.3, + linestyle="dotted", + ) + + ax[0].scatter( + theta.flatten(), f.flatten(), s=60, facecolors="none", edgecolors="green" + ) + ax[0].set_xlabel(r"$\theta$", fontsize=ft) + ax[0].set_ylabel(r"$\zeta(\theta)$", fontsize=ft) + ax[0].tick_params(axis="both", labelsize=ft) + + phat = np.zeros(theta_test.shape[0]) + phatvar = np.zeros(theta_test.shape[0]) + pvar1 = np.zeros(theta_test.shape[0]) + for tid in range(0, len(theta_test)): + rnd = sps.norm(loc=mean[0, tid], scale=np.sqrt(obsvar + var[0, tid])) + phat[tid] = rnd.pdf(real_data) + rnd = sps.norm(loc=mean[0, tid], scale=np.sqrt(0.5 * obsvar + var[0, tid])) + pvar1[tid] = rnd.pdf(real_data) + phatvar[tid] = (1 / (2 * np.sqrt(np.pi) * np.sqrt(obsvar))) * pvar1[tid] - phat[ + tid + ] ** 2 + + ax[1].plot(theta_test, p_test, color="red") + ax[1].plot(theta_test, phat, color="blue", linestyle="dashed", linewidth=2.5) + ax[1].fill_between( + theta_test.flatten(), + (phat - np.sqrt(phatvar)).flatten(), + (phat + np.sqrt(phatvar)).flatten(), + color="blue", + alpha=0.1, + ) + ax[1].set_xlabel(r"$\theta$", fontsize=ft) + ax[1].set_ylabel(r"$p(y|\theta)$", fontsize=ft) + ax[1].tick_params(axis="both", labelsize=ft) + + ax[2].plot( + theta_test.flatten(), + emupred._info["nugs"].flatten(), + color="blue", + linestyle="dashed", + linewidth=2.5, + ) + ax[2].plot( + theta_test.flatten(), cls_func.noise(theta_test).flatten(), color="black" + ) + ax[2].set_xlabel(r"$\theta$", fontsize=ft) + ax[2].set_ylabel(r"$\mathbb{V}[\nu]$", fontsize=ft) + ax[2].tick_params(axis="both", labelsize=ft) + plt.savefig("toy1.png", format="jpeg", bbox_inches="tight", dpi=1000) + plt.show() + + return theta_test, emupred._info["nugs"].flatten(), phat + + +def Figure2(desobject, theta_test, nugs, phat, method, axs): + + theta0 = desobject.theta0 + reps0 = desobject.rep0 + + ft = 15 + + def make_patch_spines_invisible(ax): + ax.set_frame_on(True) + ax.patch.set_visible(False) + for sp in ax.spines.values(): + sp.set_visible(False) + + par1 = axs.twinx() + par2 = axs.twinx() + + par2.spines["right"].set_position(("axes", 1.25)) + make_patch_spines_invisible(par2) + par2.spines["right"].set_visible(True) + + p1 = axs.scatter( + theta0.flatten(), + reps0.flatten(), + color="black", + marker="*", + s=55, + label=r"$a_i$", + ) + (p2,) = par1.plot( + theta_test.flatten(), + nugs, + "b", + linestyle="dashed", + label="Variance", + linewidth=2.5, + ) + (p3,) = par2.plot( + theta_test, phat, "r", linestyle="dotted", label="Likelihood", linewidth=2.5 + ) + + axs.set_xlim(-0.1, 1.1) + axs.set_ylim(0.8 * np.min(reps0.flatten()), 1.1 * np.max(reps0.flatten())) + par1.set_ylim(0.8 * np.min(nugs), 1.1 * np.max(nugs)) + par2.set_ylim(-0.1, 1.1 * np.max(phat)) + + axs.set_xlabel(r"$\theta$", fontsize=ft) + axs.set_ylabel(r"$a_i$", fontsize=ft) + par1.set_ylabel("Variance", fontsize=ft) + par2.set_ylabel("Likelihood", fontsize=ft) + axs.tick_params(axis="both", labelsize=ft) + par1.tick_params(axis="both", labelsize=ft) + par2.tick_params(axis="both", labelsize=ft) + lines = [p1, p2, p3] + + axs.legend( + lines, + [l.get_label() for l in lines], + loc="center", + bbox_to_anchor=(0.5, -0.3), + ncol=3, + prop={"size": ft}, + ) diff --git a/examples/Example5/README.rst b/examples/Example5/README.rst new file mode 100644 index 0000000..c74ac4c --- /dev/null +++ b/examples/Example5/README.rst @@ -0,0 +1,32 @@ +Examples +~~~~~~~~ + +This example demonstrates how to use the active learning procedure from Sürer (2025), +Active Learning for Data-Efficient Calibration of Stochastic Simulation Models. + + +**Instructions for running the illustrative examples with the active learning procedure** + +To replicate the figures below, respectively: + +1) Go to the ``examples/Example5`` directory. + +2) Execute any of the following from the command line: + +.. code-block:: python + + python example.py + +Running this script should not take more than 5 min. See the figure (png files) saved under +``examples/Example5`` directory. + +.. image:: ex5.png + :alt: Illustration of PUQ with the example + :align: center + :width: 600 + +The proposed procedure is illustrated with examples featuring different numbers +of posterior modes. Contour lines represent the posterior distribution of the +parameters, while the background color indicates the intrinsic noise. +Cyan markers indicate the points in the initial design, +while blue markers denote the acquired points, with numbers showing the replications. \ No newline at end of file diff --git a/examples/Example5/ex5.png b/examples/Example5/ex5.png new file mode 100644 index 0000000..a2e7145 Binary files /dev/null and b/examples/Example5/ex5.png differ diff --git a/examples/Example5/example.py b/examples/Example5/example.py new file mode 100644 index 0000000..38df7ce --- /dev/null +++ b/examples/Example5/example.py @@ -0,0 +1,102 @@ +import numpy as np +from utils import fig6 +from utils_sample import test_data_gen +import matplotlib.pyplot as plt +from scipy.stats import qmc +from PUQ.designmethods.sequential_1d_stochastic import sequential_design +from test_functions import unimodalx, bimodalx, braninx + +T, s = 100, 1 +# Methods to iterate over +dict_meth = { + "method": ["ivar"], + "horizon": [ + {"method": "target", "h0": 0, "target_ratio": 0.2}, + ], + "labels": ["target"], +} +fig, ax = plt.subplots(1, 3, figsize=(12, 3), constrained_layout=True) + +if __name__ == "__main__": + + for eid, example in enumerate(["unimodalx", "bimodalx", "braninx"]): + + cex = eval(example)() + cex.realdata(x=np.array([0.5])[:, None], seed=None) + + tg, fg, pg, zg, ng, t_s, p_s, w_s, f_s, n_s, Xpl, Ypl = test_data_gen( + cex, sample=True + ) + + tdat = { + "f": f_s, + "theta": t_s, + "xt": None, + "p": p_s, + "noise": n_s, + "w": w_s, + "p_prior": 1, + } + + gdat = { + "f": fg, + "theta": tg, + "xt": zg, + "p": pg, + "noise": ng, + "X": Xpl, + "Y": Ypl, + } + + # Set random stream for initial design + persis_info = {"rand_stream": np.random.default_rng(s)} + + # Initial sample + n0, rep0 = 30, 5 + sampling = qmc.LatinHypercube(d=cex.zlim.shape[0], seed=int(s)) + z0u = sampling.random(n=n0) + z0 = np.repeat(z0u, rep0, axis=0) + f0 = np.array( + [cex.sim_f(z0[i, :], persis_info=persis_info) for i in range(n0 * rep0)] + ) + + for mid, method in enumerate(dict_meth["method"]): + print(method) + # Set random stream for initial design + persis_info = {"rand_stream": np.random.default_rng(s)} + + # Set random stream for initial design + des_obj = sequential_design(cex) + des_obj.build_design( + z0=z0, + f0=f0, + T=T, + persis_info=persis_info, + test=tdat, + af="lookahead", + args={ + "horizon": dict_meth["horizon"][mid], + "long": False, + "nL": 300, + "seed": s, + "method": method, + "t_grid": None, + "integral": "importance", + }, + ) + + fig6( + des_obj, + z0[:, 1:3], + rep0, + gdat["X"], + gdat["Y"], + gdat["p"], + gdat["noise"], + ax[eid], + {}, + fig, + ) + +plt.savefig("ex5.png", format="jpeg", bbox_inches="tight", dpi=500) +plt.show() diff --git a/examples/Example5/paper_figs.py b/examples/Example5/paper_figs.py new file mode 100644 index 0000000..e9c2740 --- /dev/null +++ b/examples/Example5/paper_figs.py @@ -0,0 +1,754 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Tue May 20 14:45:08 2025 + +@author: surero +""" +import seaborn as sns +import matplotlib.pyplot as plt +from utils import read_data, read_output, read_summary_metric +import matplotlib as mpl +import pandas as pd +import numpy as np +from matplotlib.colors import ListedColormap + +# Custom yellow-orange colormap +yellow_colors = [ + (1, 1, 1), + (1, 1, 0.8), + (1, 1, 0.6), + (1, 1, 0.4), + (1, 1, 0.2), + (1, 1, 0), + (1, 0.9, 0), + (1, 0.8, 0), + (1, 0.6, 0), + (1, 0.4, 0), + (1, 0.2, 0), +] +yellow_cmap = ListedColormap(yellow_colors, name="yellow") + + +custom_palette = {"ivar": "red", "imse": "blue", "var": "green", "varx": "magenta"} +hue_order = ["ivar", "imse", "var"] + +hatch_color_map = { + (0.875, 0.125, 0.125, 1): "//", + (0.125, 0.125, 0.875, 1): "\\", + (0.06274509803921569, 0.4392156862745098, 0.06274509803921569, 1): "xx", +} + + +def lineplot( + df, + examples, + metric="TV", + ci=None, + label=None, + custom_labels=None, + ax=None, + hue=None, + figset={}, + scaled=True, +): + ifLeg = figset.get("ifLeg", True) + sety = figset.get("sety", True) + ft = figset.get("ft", 12) + lw = figset.get("lw", 3) + + for i, example in enumerate(examples): + df1 = df.loc[df["example"] == example] + + if scaled: + group_cols = ["t", hue, "h"] + mean_df = df1.groupby(group_cols)[metric].mean().reset_index() + max_val = mean_df[metric].max() + mean_df[metric] = mean_df[metric] / max_val + data = mean_df.copy() + else: + data = df1.copy() + + sns.lineplot( + data=data, + x="t", + y=metric, + hue=hue, + style="h", + palette=custom_palette, + errorbar=ci, + linewidth=lw, + ax=ax, + ) + + lgd = ax.legend( + loc="upper center", + bbox_to_anchor=(1.3, 1), + fancybox=True, + shadow=True, + ncol=1, + fontsize=ft, + ) + + ax.set_yscale("log") + ax.set_xlabel("t", fontsize=ft) + if sety == False: + ax.set_ylabel("") + else: + ax.set_ylabel(metric, fontsize=ft) + ax.tick_params(axis="both", labelsize=ft) + if ifLeg == False: + ax.legend().remove() + + +def boxplot(df, ax, x, hue, figset={}): + ifLeg, sety = figset.get("ifLeg", True), figset.get("sety", True) + ft = figset.get("ft", 12) + + sns.boxplot( + x=x, + y="percent", + hue="method", + data=df, + ax=ax, + showfliers=False, + palette=custom_palette, + hue_order=hue_order, + ) + + # select the correct patches + patches = [patch for patch in ax.patches if type(patch) == mpl.patches.PathPatch] + + # iterate through the patches for each subplot + for patch in patches: + fc = patch.get_facecolor() + patch.set_edgecolor(fc) + patch.set_facecolor("none") + patch.set_hatch(hatch_color_map[fc]) + + # # # Fix legend to match hatches and colors + handles, labels = ax.get_legend_handles_labels() + legend = ax.legend( + handles, + labels, + title="method", + loc="center left", + bbox_to_anchor=(1.02, 0.5), + borderaxespad=0.0, + ) + + for patch in legend.get_patches(): + fc = patch.get_facecolor() + patch.set_edgecolor(fc) + patch.set_facecolor("none") + patch.set_hatch(hatch_color_map[fc]) + + if sety == False: + ax.set_ylabel("") + else: + ax.set_ylabel("Percent of exploration", fontsize=ft) + + plt.xticks(fontsize=ft) + + if ifLeg == False: + ax.legend().remove() + + +def plot_experiment_figure( + examples, + path, + ms, + ls, + lg, + ly, + reps=(1, 31), + metric="MAD", + layout="2xN", + figsize=(12, 6), + ft=10, + r0=5, + n0=150, + title=None, +): + + single_example = len(examples) == 1 + if layout == "1x2" and single_example: + fig, ax = plt.subplots(1, 2, figsize=figsize) + elif layout == "2xN": + fig, ax = plt.subplots(2, len(examples), figsize=figsize) + else: + raise ValueError("Unsupported layout or mismatched number of examples.") + + for i, ex in enumerate(examples): + print(ex) + figset = {"ifLeg": lg[i], "sety": ly[i], "ft": ft, "r0": r0, "n0": n0} + df1 = read_data( + rep0=reps[0], + repf=reps[1], + methods=ms, + examples=[ex], + folderpath=path, + label=ls, + ) + print(df1["s"]) + df2 = ( + df1.groupby(["s", "h", "example", "method"])["new"] + .mean() + .mul(100) + .reset_index() + ) + df2.rename(columns={"new": "percent"}, inplace=True) + + if layout == "1x2": + lineplot(df1, [ex], metric=metric, hue="method", ax=ax[0], figset=figset) + boxplot_alternative(path, ms, ls, ex, ax=ax[1], figset=figset) + # boxplot(df=df2, ax=ax[1], x="h", hue="method", figset=figset) + else: # layout == "2xN" + lineplot(df1, [ex], metric=metric, hue="method", ax=ax[0, i], figset=figset) + boxplot_alternative(path, ms, ls, ex, ax=ax[1, i], figset=figset) + # boxplot(df=df2, ax=ax[1, i], x="h", hue="method", figset=figset) + + plt.tight_layout(pad=0.2) + if title is not None: + plt.savefig(title, dpi=300, bbox_inches="tight") + plt.show() + + +def plot_SIR_experiment_figure( + path, + ms, + ls, + reps=(1, 31), + metric="MAD", + figsize=(12, 6), + ft=10, + figset={}, + title=None, +): + usual_boxplot = figset.get("usual_boxplot", True) + shift, scaled = figset.get("shift_x", 0), figset.get("scaled", True) + + fig, ax = plt.subplots(1, 4, figsize=figsize, gridspec_kw={"wspace": 0.25}) + # , "width_ratios": [1.25, 1.25, 1, 1] + lw = 3 + + df1 = read_data( + rep0=reps[0], + repf=reps[1], + methods=ms, + examples=["SIR"], + folderpath=path, + label=ls, + ) + print(df1["s"]) + df3 = df1.loc[df1["t"] == 199] + + if scaled: + group_cols = ["t", "method", "h"] + mean_df = df1.groupby(group_cols)[metric].mean().reset_index() + max_val = mean_df[metric].max() + mean_df[metric] = mean_df[metric] / max_val + data = mean_df.copy() + else: + data = df1.copy() + + # Lineplot + sns.lineplot( + data=data, + x="t", + y=metric, + hue="method", + style="h", + palette=custom_palette, + errorbar=None, + linewidth=lw, + ax=ax[0], + ) + ax[0].legend( + loc="upper center", + bbox_to_anchor=(0.5, -0.2), + fancybox=True, + shadow=True, + ncol=2, + fontsize=ft, + ) + ax[0].set_yscale("log") + + if usual_boxplot: + df2 = ( + df1.groupby(["s", "h", "example", "method"])["new"] + .mean() + .mul(100) + .reset_index() + ) + df2.rename(columns={"new": "percent"}, inplace=True) + box_data = [ + (df2, "percent", ax[1], "percent"), + (df3, "MADy", ax[2], r"$MAD^y$"), + (df3, "MADn", ax[3], r"$MAD^n$"), + ] + else: + figset["legend_right"] = False + boxplot_alternative( + path, ms, ls, "SIR", ax=ax[1], figset=figset + ) # {"legend_right":False}) + box_data = [ + (df3, "MADy", ax[2], r"$MAD^y$"), + (df3, "MADn", ax[3], r"$MAD^n$"), + ] + + for boxid, box in enumerate(box_data): + sns.boxplot( + x=box[1], + y="h", + hue="method", + data=box[0], + showfliers=False, + palette=custom_palette, + hue_order=hue_order, + ax=box[2], + ) + + # select the correct patches + patches = [ + patch for patch in box[2].patches if type(patch) == mpl.patches.PathPatch + ] + + # iterate through the patches for each subplot + for patch in patches: + fc = patch.get_facecolor() + patch.set_edgecolor(fc) + patch.set_facecolor("none") + patch.set_hatch(hatch_color_map[fc]) + + box[2].legend_.remove() + + # # # Fix legend to match hatches and colors + if boxid == 1: + handles, labels = box[2].get_legend_handles_labels() + legend = box[2].legend( + handles, + labels, + title="method", + loc="lower center", + bbox_to_anchor=(0.5 + shift, -0.5), + ncol=3, + ) + box[2].set_yticks([]) + + for patch in legend.get_patches(): + fc = patch.get_facecolor() + patch.set_edgecolor(fc) + patch.set_facecolor("none") + patch.set_hatch(hatch_color_map[fc]) + + for label in box[2].get_yticklabels(): + label.set_rotation(90) + + box[2].set_ylabel("") + box[2].set_xlabel(box[3], fontsize=ft) + + if title is not None: + plt.savefig(title, dpi=300, bbox_inches="tight") + plt.show() + + +def plot_summary_metric(path, examples, methods, ls, metrics, rep=(1, 31)): + + df = read_summary_metric(path, examples, methods, ls, rep) + + for metric in metrics: + fig, ax = plt.subplots(1, 1, figsize=(6, 4), constrained_layout=True) + sns.lineplot( + data=df, + x="t", + y=metric, + hue="method", + style="h", + ax=ax, + ) + ax.set_yscale("log") + plt.show() + + +def plot_sir_figure( + path, + label="adapt", + methods=["ivar", "imse", "var"], + ft=12, + repid=(1, 2), + title=None, +): + + # Prepare data + from utils_SIR import test_gen_SIR + from SIR_funcs import SIRx + + cls_func = SIRx() + cls_func.realdata(x=np.array([[0.25, 0.25], [0.75, 0.75]]), seed=None) + p_grid, t_grid, noise_grid, Xpl, Ypl = test_gen_SIR(cls_func, return_XY=False) + + for r in np.arange(repid[0], repid[1]): + # Plotting + fig, ax = plt.subplots( + 1, len(methods), figsize=(11, 3), constrained_layout=True + ) + + for mid, method in enumerate(methods): + desobj = read_output(path, "SIR", method, r, label=label) + + # Contour plots + ax[mid].contour(Xpl, Ypl, p_grid.reshape(50, 50), cmap="coolwarm", zorder=2) + cs = ax[mid].contourf( + Xpl, + Ypl, + np.sum(noise_grid, axis=1).reshape(50, 50), + cmap=yellow_cmap, + alpha=0.75, + zorder=1, + ) + + # Add colorbar only to the last axis + if mid == len(methods) - 1: + fig.colorbar(cs, ax=ax[mid], pad=0.1) + + # Annotate sample locations and frequency + unique_rows, counts = np.unique( + desobj.zs[200:, 2:4], axis=0, return_counts=True + ) + print(counts) + for lbl, x, y in zip(counts, unique_rows[:, 0], unique_rows[:, 1]): + ax[mid].annotate( + lbl, + xy=(x, y), + xytext=(0, 0), + textcoords="offset points", + fontsize=ft, + color="blue", + zorder=3, + ) + + # Axis formatting + ax[mid].set_xticks([0, 0.5, 1]) + ax[mid].set_xlabel(r"$\theta_1$", fontsize=ft + 2) + ax[mid].tick_params(axis="both", labelsize=ft + 2) + if mid == 0: + ax[mid].set_yticks([0, 0.5, 1]) + ax[mid].set_ylabel(r"$\theta_2$", fontsize=ft + 2) + else: + ax[mid].set_yticks([]) + + if title is not None: + plt.savefig(title, dpi=300, bbox_inches="tight") + plt.show() + + +def plot_pritam(path, repid=1, label="target", title=None): + from test_functions import pritam + from utils_sample import test_data_gen_pri + + # Plot settings + ft = 12 + ms = ["ivar", "imse", "var"] + n0 = 150 + + # Define test points + x = np.array([0.2, 0.8]) + y = np.array([0.2, 0.8]) + xr = np.array([[xx, yy] for xx in x for yy in y]) + + # Instantiate test function object and generate real data + cex = pritam() + cex.realdata(x=xr, seed=None) + + # Generate test grid + nmesh = 50 + x1 = np.linspace(cex.zlim[0][0], cex.zlim[0][1], nmesh) + x2 = np.linspace(cex.zlim[1][0], cex.zlim[1][1], nmesh) + X1, X2 = np.meshgrid(x1, x2) + Xg = np.vstack([X1.ravel(), X2.ravel()]).T + + # Evaluate noise and function values on the grid + ng = np.array([cex.noise(x[0], x[1], cex.theta_true[0]) for x in Xg]) + fg = np.array([cex.function(x[0], x[1], cex.theta_true[0]) for x in Xg]) + + # Create subplots + fig, ax = plt.subplots(2, 3, figsize=(9, 5), constrained_layout=True) + + for mid, m in enumerate(ms): + desobj = read_output(path, "pritam", m, repid, label="target") + + # Top row: histogram of theta samples + ax[0, mid].hist( + desobj.zs[n0:, 2], bins=30, edgecolor="black", alpha=0.75, color="blue" + ) + ax[0, mid].set_xlim(0, 1) + ax[0, mid].set_xlabel(r"$\vartheta$", fontsize=ft) + if mid == 0: + ax[0, mid].set_ylabel("Frequency", fontsize=ft) + + # Bottom row: scatter plot of x-samples with contour background + ax[1, mid].scatter( + desobj.zs[n0:, 0], + desobj.zs[n0:, 1], + marker="o", + facecolors="none", + edgecolors="blue", + zorder=2, + ) + + # Overlay test design points + for xpt in xr: + ax[1, mid].scatter( + xpt[0], xpt[1], marker="x", color="black", s=100, zorder=3 + ) + + # Contour of noise + cs = ax[1, mid].contourf( + X1, X2, ng.reshape(nmesh, nmesh), cmap=yellow_cmap, alpha=0.75, zorder=1 + ) + + ax[1, mid].set_xlabel(r"$x_1$", fontsize=ft) + ax[1, mid].set_ylabel(r"$x_2$", fontsize=ft) + ax[1, mid].set_xticks([0, 0.5, 1]) + ax[1, mid].set_yticks([0, 0.5, 1]) + + if title is not None: + plt.savefig(title, dpi=300, bbox_inches="tight") + plt.show() + + +def plot_horizon_progress( + path, + example="SIR", + method="ivar", + single_seed=1, + labels=("target", "adapt"), + reps=(1, 31), + figsize=(8, 2), + colors=("gray", "orange"), + title=None, +): + + fig, ax = plt.subplots(1, 2, figsize=figsize) + + for mid, label in enumerate(labels): + # Read and filter data + df = read_data( + rep0=reps[0], + repf=reps[1], + methods=[method], + examples=[example], + folderpath=path, + label=[label], + ) + + df_label = df.loc[df["h"] == label].copy() + df_label["new_cumsum"] = df_label.groupby("s")["new"].cumsum() + + # Plot for a single seed + df_seed1 = df_label[df_label["s"] == single_seed] + ax[0].plot(df_seed1["t"], df_seed1["horizon"], color=colors[mid], linestyle=":") + + # Median horizon across reps + df_median = df_label.groupby("t", as_index=False)["horizon"].mean() + ax[0].plot( + df_median["t"], + df_median["horizon"], + color=colors[mid], + label=label, + linewidth=3, + ) + + # Mean cumulative new per t + df_cumsum_avg = df_label.groupby("t", as_index=False)["new_cumsum"].mean() + ax[1].plot( + df_cumsum_avg["t"], + df_cumsum_avg["new_cumsum"] / np.arange(1, len(df_cumsum_avg) + 1), + color=colors[mid], + linewidth=3, + label=label, + ) + + # Axis labels and legends + ax[0].set_xlabel("t") + ax[0].set_ylabel("Horizon") + ax[0].legend() + + ax[1].set_xlabel("t") + ax[1].set_ylabel(r"$n_t / \sum_{i=1}^{n_t} a_i$") + ax[1].legend() + plt.tight_layout() + + if title is not None: + plt.savefig(title, dpi=300, bbox_inches="tight") + + plt.show() + + +def plot_pairwise_from_output(path, example, ms, n0=90, rep0=5, d=2, label="h=-1"): + + for m in ms: + desobj = read_output(path, example, m, 1, label=label) + df = pd.DataFrame(desobj.zs[(n0 * rep0) :, d:]) + sns.pairplot(df, diag_kind="kde") + plt.suptitle(f"m = {m}", y=1.02) + plt.tight_layout() + plt.show() + + +def boxplot_alternative(path, methods, horizons, example, ax, figset={}): + ifLeg, sety = figset.get("ifLeg", True), figset.get("sety", True) + legend_right = figset.get("legend_right", True) + ft = figset.get("ft", 10) + r0, n0 = figset.get("r0", 5), figset.get("n0", 50) + + lst = [] + for m in methods: + for hor in horizons: + for i in np.arange(1, 31): + desobj = read_output(path, example, m, i, label=hor) + + # Eliminate the initial sample + initial_des = desobj.zs[0:n0, :] + unique_init, counts_init = np.unique( + initial_des, axis=0, return_counts=True + ) + + # Convert unique_init to a set of tuple rows for fast lookup + unique_init_set = set(map(tuple, unique_init)) + + count_reps = [] + for xtid, xt in enumerate(desobj.xt): + xt_tuple = tuple(xt) + if (xt_tuple in unique_init_set) and (desobj.reps[xtid] == r0): + continue # skip this one + count_reps.append(desobj.reps[xtid]) + + count_reps = np.array(count_reps) + total = len(count_reps) + # Count percentages + percent_1 = np.sum(count_reps == 1) / total * 100 + percent_2 = np.sum(count_reps == 2) / total * 100 + percent_3 = np.sum(count_reps == 3) / total * 100 + percent_4 = np.sum(count_reps == 4) / total * 100 + percent_5 = np.sum(count_reps >= 5) / total * 100 + + lst.append( + { + "1": percent_1, + "2": percent_2, + "3": percent_3, + "4": percent_4, + "5": percent_5, + "r": i, + "method": m, + "h": hor, + } + ) + + df = pd.DataFrame(lst) + + # Average over r before plotting + components = ["1", "2", "3", "4", "5"] + + df_avg = df.groupby(["h", "method"])[components].mean().reset_index() + + # Prepare for plotting + h_values = sorted(df_avg["h"].unique()) + methods_u = df_avg["method"].unique() + num_methods = len(methods_u) + + bar_width = 0.1 + group_spacing = 0.1 + hatches = ["/", "\\", "x", "o", "."] + colors = ["blue", "green", "orange", "red", "purple"] + + # Create x positions + x_positions = [] + current_x = 0 + grouped = df_avg.sort_values(["h", "method"]).reset_index(drop=True) + + for i, h in enumerate(h_values): + methods_in_group = grouped[grouped["h"] == h] + for _ in range(len(methods_in_group)): + x_positions.append(current_x) + current_x += bar_width + current_x += group_spacing + + # Plot + # fig, ax = plt.subplots(figsize=(6, 6)) + bottoms = np.zeros(len(grouped)) + + # First pass: fill with white so we can hatch over it + for i, comp in enumerate(components): + ax.bar( + x_positions, + grouped[comp], + bottom=bottoms, + width=bar_width, + color="white", + edgecolor="black", + ) + bottoms += grouped[comp].values + + # Second pass: hatch overlay with colored edges + bottoms = np.zeros(len(grouped)) + for i, comp in enumerate(components): + ax.bar( + x_positions, + grouped[comp], + bottom=bottoms, + width=bar_width, + fill=False, + hatch=hatches[i % len(hatches)], + edgecolor=colors[i % len(colors)], + linewidth=1.5, + label=f"{comp}", + ) + bottoms += grouped[comp].values + + # Method labels on x-axis + ax.set_xticks(x_positions) + ax.set_xticklabels(grouped["method"].values, rotation=45, ha="right") + + # h group labels below the x-axis + for i, h in enumerate(h_values): + group_start = i * (num_methods * bar_width + group_spacing) + group_mid = group_start + (num_methods - 1) * bar_width / 2 + ax.text( + group_mid, + -0.002 * ax.get_ylim()[1], + f"h = {h}", + ha="center", + va="top", + fontsize=ft, + transform=ax.get_xaxis_transform(), + ) + + if legend_right: + ax.legend( + loc="upper center", + bbox_to_anchor=(1.3, 1), + fancybox=True, + shadow=True, + ncol=1, + fontsize=ft, + ) + else: + ax.legend( + loc="lower center", + bbox_to_anchor=(0.5, -0.6), + fancybox=True, + shadow=True, + ncol=3, + fontsize=ft, + ) + + if sety == False: + ax.set_ylabel("") + else: + ax.set_ylabel("Percentage (%)", fontsize=ft) + ax.tick_params(axis="both", labelsize=ft) + if ifLeg == False: + ax.legend().remove() diff --git a/examples/Example5/test_functions.py b/examples/Example5/test_functions.py new file mode 100644 index 0000000..50232c7 --- /dev/null +++ b/examples/Example5/test_functions.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Wed Feb 12 13:13:23 2025 + +@author: surero +""" +import numpy as np +import scipy + + +class sinfunc: + def __init__(self): + self.data_name = "sinfunc" + self.zlim = np.array([[0, 1], [0, 1]]) + self.theta_true = np.array([0.5]) # np.array([np.pi / 5]) + self.real_data = None + self.out = [("f", float)] + self.d = 1 + self.p = 2 + self.dx = 1 + self.dt = 1 + self.x = None + self.sigma2 = 0.1 + + def function(self, x, theta): + f = np.sin(10 * x - 5 * theta) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1]) + var_noise = self.noise(thetas[0], thetas[1]) + noise = persis_info["rand_stream"].normal(0, np.sqrt(var_noise), 1) + f += noise + return f + + def realdata(self, x, seed): + self.x = x + self.d = len(x) + self.obsvar = np.diag(np.repeat(self.sigma2, self.d)) + + M = np.array( + [self.function(x, self.theta_true) for x in self.x], dtype=float + ).reshape(1, self.d) + if seed is not None: + rand_stream = np.random.default_rng(seed) + R = rand_stream.normal(0, np.sqrt(self.sigma2), size=(1, self.d)) + self.real_data = M + R + else: + self.real_data = M + + def noise(self, x, theta): + alpha = 10 + min_value = 0.01 # 0.05 + max_value = 0.1 # 0.3 + + weight = 1 / (1 + np.exp(-alpha * (x - 0.5))) # Sigmoid transition + value = min_value + (max_value - min_value) * weight # Scale between 0.1 and 10 + return value + + +class unimodalx: + def __init__(self): + self.data_name = "unimodalx" + self.zlim = np.array([[0, 1], [0, 1], [0, 1]]) + self.real_data = None + self.out = [("f", float)] + self.d = 1 + self.p = 3 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.5, 0.5]) + self.sigma2 = 0.1 + self.dx = 1 + self.dt = 2 + + def function(self, x, t1, t2): + t1 = -10 + t1 * 20 + t2 = -10 + t2 * 20 + f = 0.26 * (t1**2 + t2**2) - 0.48 * t1 * t2 + (2 * x - 1) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1], thetas[2]) + V = self.noise(thetas[0], thetas[1], thetas[2]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V), 1) + f += R + return f + + def noise(self, x, t1, t2): + V = 0.01 + 1.2 * (t1**2 + t2**2) * 2 + V = V + return V + + def realdata(self, x, seed): + self.x = x + self.d = len(x) + self.obsvar = np.diag(np.repeat(self.sigma2, self.d)) + M = np.array([self.function(x, *self.theta_true) for x in self.x], dtype=float) + if seed is not None: + rand_stream = np.random.default_rng(seed) + R = rand_stream.normal(0, np.sqrt(self.sigma2), size=(1, self.d)) + self.real_data = M + R + else: + self.real_data = M + + +class bimodalx: + def __init__(self): + self.data_name = "bimodalx" + self.zlim = np.array([[0, 1], [0, 1], [0, 1]]) + self.theta_true = np.array([0.35, 0.35]) + self.real_data = None + self.out = [("f", float)] + self.p = 3 + self.d = 1 + self.x = np.arange(0, self.d)[:, None] + self.sigma2 = 0.05 + self.dx = 1 + self.dt = 2 + + def function(self, x, t1, t2): + mu1 = (0.35, 0.35) + mu2 = (0.65, 0.65) + sigma = 0.15 + term1 = np.exp(-((t1 - mu1[0]) ** 2 + (t2 - mu1[1]) ** 2) / sigma**2) + term2 = np.exp(-((t1 - mu2[0]) ** 2 + (t2 - mu2[1]) ** 2) / sigma**2) + f = (term1 + term2) + (2 * x - 1) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1], thetas[2]) + V = self.noise(thetas[0], thetas[1], thetas[2]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V), 1) + f += R + return f + + def noise(self, x, t1, t2): + cov = np.array([[0.05, 0], [0, 0.05]]) + var = scipy.stats.multivariate_normal(mean=[0.85, 0.85], cov=cov) + return 0.1 * var.pdf(np.array([t1, t2])) + + def realdata(self, x, seed): + self.x = x + self.d = len(x) + self.obsvar = np.diag(np.repeat(self.sigma2, self.d)) + M = np.array([self.function(x, *self.theta_true) for x in self.x], dtype=float) + if seed is not None: + rand_stream = np.random.default_rng(seed) + R = rand_stream.normal(0, np.sqrt(self.sigma2), size=(1, self.d)) + self.real_data = M + R + else: + self.real_data = M + + +class braninx: + def __init__(self): + self.data_name = "braninx" + self.zlim = np.array([[0, 1], [0, 1], [0, 1]]) + self.real_data = None + self.out = [("f", float)] + self.d = 1 + self.p = 3 + self.x = np.arange(0, self.d)[:, None] + self.theta_true = np.array([0.9613333333333334, 0.16466666666666668]) + self.sigma2 = 5 + self.dx = 1 + self.dt = 2 + + def function(self, x, t1, t2): + t1 = -5 + 15 * t1 + t2 = 15 * t2 + f = ( + (t2 - (5.1 / (4 * np.pi**2)) * (t1**2) + (5 / np.pi) * t1 - 6) ** 2 + + 10 * (1 - 1 / (8 * np.pi)) * np.cos(t1) + + 10 + + (2 * x - 1) + ) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1], thetas[2]) + V = self.noise(thetas[0], thetas[1], thetas[2]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V), 1) + f += R + return f + + def noise(self, x, t1, t2): + alpha = 10 + min_value = 0.1 + max_value = 15 + + weight = 1 / (1 + np.exp(-alpha * (t2 - 0.5))) + value = min_value + (max_value - min_value) * weight + + return value + + def realdata(self, x, seed): + self.x = x + self.d = len(x) + self.obsvar = np.diag(np.repeat(self.sigma2, self.d)) + M = np.array([self.function(x, *self.theta_true) for x in self.x], dtype=float) + if seed is not None: + rand_stream = np.random.default_rng(seed) + R = rand_stream.normal(0, np.sqrt(self.sigma2), size=(1, self.d)) + self.real_data = M + R + else: + self.real_data = M + + +class pritam: + def __init__(self): + self.data_name = "pritam" + self.zlim = np.array([[0, 1], [0, 1], [0, 1]]) + self.theta_true = np.array([0.5]) + self.out = [("f", float)] + self.d = 1 + self.p = 3 + self.real_data = None + self.dx = 2 + self.sigma2 = 10 # 0.5**2 + self.dx = 2 + self.dt = 1 + + def function(self, x1, x2, theta1): + f = (30 + 5 * x1 * np.sin(5 * x1)) * (6 * theta1 + 1 + np.exp(-5 * x2)) + return f + + def sim_f(self, thetas, persis_info): + f = self.function(thetas[0], thetas[1], thetas[2]) + V = self.noise(thetas[0], thetas[1], thetas[2]) + R = persis_info["rand_stream"].normal(0, np.sqrt(V), 1) + f += R + return f + + def realdata(self, x, seed): + self.x = x + self.d = len(x) + self.obsvar = np.diag(np.repeat(self.sigma2, self.d)) + M = np.array( + [self.function(x[0], x[1], self.theta_true) for x in self.x], dtype=float + ).reshape(1, self.d) + if seed is not None: + rand_stream = np.random.default_rng(seed) + R = rand_stream.normal(0, np.sqrt(self.sigma2), size=(1, self.d)) + self.real_data = M + R + else: + self.real_data = M + + def noise(self, x1, x2, t1): + # return 5 + + cov = np.array([[0.1, 0], [0, 0.1]]) + var = scipy.stats.multivariate_normal(mean=[0.5, 0.5], cov=cov) + return (15 * t1) * var.pdf(np.array([x1, x2])) diff --git a/examples/Example5/toy_example.py b/examples/Example5/toy_example.py new file mode 100644 index 0000000..ffe6468 --- /dev/null +++ b/examples/Example5/toy_example.py @@ -0,0 +1,100 @@ +import numpy as np +from test_functions import sinfunc +import scipy.stats as sps +from smt.sampling_methods import LHS +from PUQ.designmethods.sequential_1d_stochastic import sequential_design +from utils import toy_example, visual_xt + +maxiter = 50 +new = False +seedmin, seedmax = 2, 3 +# seedmin, seedmax = 1, 2 +dfl = [] + +if __name__ == "__main__": + + for s in np.arange(seedmin, seedmax): + + cls_data = sinfunc() + dt = len(cls_data.theta_true) + cls_data.realdata(x=np.array([0.22222222, 0.88888889])[:, None], seed=None) + + # test data + nmesh, nt = 50, 100 + xpl = np.linspace(cls_data.zlim[0][0], cls_data.zlim[0][1], nmesh) + ypl = np.linspace(cls_data.zlim[1][0], cls_data.zlim[1][1], nmesh) + Xpl, Ypl = np.meshgrid(xpl, ypl) + zg = np.vstack([Xpl.ravel(), Ypl.ravel()]).T + fg = np.array([cls_data.function(*xt) for xt in zg])[:, None] + ng = np.array([cls_data.noise(*xt) for xt in zg])[:, None] + + tg = np.linspace(cls_data.zlim[1][0], cls_data.zlim[1][1], nt)[:, None] + feval = np.array( + [[cls_data.function(x, t) for x in cls_data.x] for t in tg] + ).squeeze() + pg = np.array( + [ + sps.multivariate_normal(mean=f, cov=cls_data.obsvar).pdf( + cls_data.real_data + ) + for f in feval + ] + )[:, None] + + # Set random stream for initial design + persis_info = {"rand_stream": np.random.default_rng(s)} + + # Visualize + toy_example(cls_data, Xpl, Ypl, fg, ng, persis_info) + + if new: + # Initial sample + n0, rep0 = 20, 5 + sampling = LHS(xlimits=cls_data.zlim, random_state=int(s)) + z0u = sampling(n0) + z0 = np.repeat(z0u, rep0, axis=0) + f0 = np.array([cls_data.sim_f(z, persis_info=persis_info) for z in z0]) + else: + # Initial sample + grid_size = 10 + rep0 = 5 + z1 = np.linspace(cls_data.zlim[1][0], cls_data.zlim[1][1], grid_size) + z2 = np.linspace(cls_data.zlim[1][0], cls_data.zlim[1][1], grid_size) + zm1, zm2 = np.meshgrid(z1, z2) + z0u = np.vstack([zm1.ravel(), zm2.ravel()]).T + z0 = np.repeat(z0u, rep0, axis=0) + f0 = np.array([cls_data.sim_f(z, persis_info=persis_info) for z in z0]) + + test = { + "f": fg, + "theta": tg, + "xt": zg, + "p": pg, + "noise": ng, + "p_prior": 1, + "w": 1, + } + + # Methods to iterate over + methods = ["ivar"] + for mid, method in enumerate(methods): + des_obj = sequential_design(cls_data) + des_obj.build_design( + z0=z0, + f0=f0, + T=maxiter, + persis_info=persis_info, + test=test, + af=method, + args={ + "new": new, + "nL": 200, + "seed": s, + "method": method, + "t_grid": tg, + "neighbor": "LHS", + "extra_metric": False, + }, + ) + + visual_xt(cls_data, des_obj, z0u, rep0, tg, pg, ng, Xpl, Ypl, new) diff --git a/examples/Example5/utils.py b/examples/Example5/utils.py new file mode 100644 index 0000000..76a5ec7 --- /dev/null +++ b/examples/Example5/utils.py @@ -0,0 +1,400 @@ +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.colors import ListedColormap +import pandas as pd +import matplotlib as mpl +import matplotlib.patches as mpatches + +yellow_colors = [ + (1, 1, 1), + (1, 1, 0.8), # light yellow + (1, 1, 0.6), + (1, 1, 0.4), + (1, 1, 0.2), + (1, 1, 0), # yellow + (1, 0.8, 0), # yellow-orange + (1, 0.6, 0), # orange + (1, 0.4, 0), # dark orange + (1, 0.2, 0), # very dark orange + (1, 0, 0), # very dark orange +] +yellow_cmap = ListedColormap(yellow_colors, name="yellow") + + +def heatmap(Xpl, Ypl, noise_grid, f_grid, p_grid, t_sample=None): + + fig, ax = plt.subplots(1, 2, figsize=(12, 4)) + cs = ax[0].contourf( + Xpl, Ypl, noise_grid.reshape(50, 50), cmap=yellow_cmap, alpha=0.75 + ) + cbar = fig.colorbar(cs) + CS = ax[0].contour(Xpl, Ypl, f_grid.reshape(50, 50), colors="black") + ax[0].clabel(CS, inline=True, fontsize=10) + ax[0].set_xlabel(r"$\theta_1$", fontsize=16) + ax[0].set_ylabel(r"$\theta_2$", fontsize=16) + ax[0].tick_params(axis="both", labelsize=16) + + cs = ax[1].contourf( + Xpl, Ypl, noise_grid.reshape(50, 50), cmap=yellow_cmap, alpha=0.75 + ) + cbar = fig.colorbar(cs) + CS = ax[1].contour(Xpl, Ypl, p_grid.reshape(50, 50), cmap="coolwarm") + if t_sample is not None: + ax[1].scatter(t_sample[:, 0], t_sample[:, 1]) + ax[1].clabel(CS, inline=True, fontsize=10) + ax[1].set_xlabel(r"$\theta_1$", fontsize=16) + ax[1].set_ylabel(r"$\theta_2$", fontsize=16) + ax[1].tick_params(axis="both", labelsize=16) + plt.show() + + +def heatmap_pritam(cls_data): + # test data + nmesh = 50 + x1 = np.linspace(cls_data.zlim[0][0], cls_data.zlim[0][1], nmesh) + x2 = np.linspace(cls_data.zlim[1][0], cls_data.zlim[1][1], nmesh) + X1, X2 = np.meshgrid(x1, x2) + Xg = np.vstack([X1.ravel(), X2.ravel()]).T + # cls_data.theta_true[0] + ng = np.array([cls_data.noise(x[0], x[1], cls_data.theta_true[0]) for x in Xg]) + fg = np.array([cls_data.function(x[0], x[1], cls_data.theta_true[0]) for x in Xg]) + + fig, ax = plt.subplots(1, 2, figsize=(12, 4)) + cs = ax[0].contourf(X1, X2, fg.reshape(50, 50), cmap=yellow_cmap, alpha=0.75) + cbar = fig.colorbar(cs) + # ax[1].clabel(CS, inline=True, fontsize=10) + for x in cls_data.x: + ax[0].scatter(x[0], x[1], marker="*", color="black") + ax[0].set_xlabel(r"$x_1$", fontsize=16) + ax[0].set_ylabel(r"$x_2$", fontsize=16) + ax[0].tick_params(axis="both", labelsize=16) + + cs = ax[1].contourf(X1, X2, ng.reshape(50, 50), cmap=yellow_cmap, alpha=0.75) + cbar = fig.colorbar(cs) + # ax[0].clabel(CS, inline=True, fontsize=10) + for x in cls_data.x: + ax[1].scatter(x[0], x[1], marker="*", color="black") + ax[1].set_xlabel(r"$x_1$", fontsize=16) + ax[1].set_ylabel(r"$x_2$", fontsize=16) + ax[1].tick_params(axis="both", labelsize=16) + plt.show() + + +def toy_example(cls_data, Xpl, Ypl, fg, ng, persis_info): + + from mpl_toolkits.axes_grid1 import make_axes_locatable + + ft = 12 + fig, axs = plt.subplots( + 1, 3, figsize=(14, 3.5), constrained_layout=False + ) # Disable constrained_layout + fig.subplots_adjust( + wspace=0.4, left=0.1, right=0.9 + ) # Adjust space between subplots + + # Contour plot for axs[1] + divider = make_axes_locatable(axs[0]) + cax1 = divider.append_axes( + "right", size="5%", pad=0.05 + ) # Adjust pad for the colorbar + cs1 = axs[0].contourf(Xpl, Ypl, fg.reshape(50, 50), cmap=yellow_cmap, alpha=0.75) + fig.colorbar(cs1, cax=cax1) + + xt_joint = np.column_stack( + (cls_data.x, np.repeat(cls_data.theta_true, len(cls_data.x))) + ) + axs[0].scatter(xt_joint[:, 0], xt_joint[:, 1], marker="x", c="black") + axs[0].set_xlabel(r"$x$", fontsize=ft) + axs[0].set_ylabel(r"$\vartheta$", fontsize=ft) + axs[0].tick_params(axis="both", labelsize=ft) + + # Contour plot for axs[2] + divider = make_axes_locatable(axs[1]) + cax2 = divider.append_axes( + "right", size="5%", pad=0.05 + ) # Adjust pad for the colorbar + cs2 = axs[1].contourf(Xpl, Ypl, ng.reshape(50, 50), cmap=yellow_cmap, alpha=0.75) + fig.colorbar(cs2, cax=cax2) + + axs[1].scatter(xt_joint[:, 0], xt_joint[:, 1], marker="x", c="black") + axs[1].set_xlabel(r"$x$", fontsize=ft) + axs[1].set_ylabel(r"$\vartheta$", fontsize=ft) + axs[1].tick_params(axis="both", labelsize=ft) + + # Scatter plot for axs[0] + xs = np.linspace(cls_data.zlim[0][0], cls_data.zlim[0][1], 100) + for k in range(10): + axs[2].scatter( + xs, + np.array( + [ + cls_data.sim_f(np.array([x, cls_data.theta_true[0]]), persis_info) + for x in xs + ] + ), + facecolors="none", + edgecolors="green", + s=80, + linewidth=2, + ) + axs[2].plot( + xs, + np.array([cls_data.function(x, cls_data.theta_true[0]) for x in xs]), + linestyle="dotted", + linewidth=3, + color="black", + ) + axs[2].set_xlabel(r"$x$", fontsize=ft) + # axs[2].set_ylabel(r"$\theta$", fontsize=ft) + axs[2].tick_params(axis="both", labelsize=ft) + + plt.show() + + +def visual_xt(cls_data, des_obj, z0u, rep0, tg, pg, ng, Xpl, Ypl, new=True): + + z, reps = des_obj.xt, des_obj.reps + + ft = 14 + fig, ax = plt.subplots(figsize=(5, 4)) + + for xitem in cls_data.x: + ax.vlines( + xitem, 0, 1, linestyles="dotted", colors="green", linewidth=3, zorder=5 + ) + for label, x_count, y_count in zip(reps, z[:, 0], z[:, 1]): + if new: + if np.any(np.all(z0u == np.array([x_count, y_count]), axis=1)): + col = "cyan" + else: + col = "blue" + else: + if label > rep0: + col = "blue" + else: + col = "cyan" + + plt.annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=ft - 2, + color=col, + weight="bold", + zorder=6, + ) + + ax.contourf(Xpl, Ypl, ng.reshape(50, 50), cmap=yellow_cmap, alpha=0.75) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.set_xlabel(r"$x$", fontsize=ft) + ax.set_ylabel(r"$\theta$", fontsize=ft) + ax.tick_params(axis="both", labelsize=ft) + ax2 = ax.twiny() + ax2.plot(pg, tg, color="black", label="Y-axis scatter") + ax2.set_xlabel(r"$p(y|\theta)$", fontsize=ft) + ax2.tick_params(axis="both", labelsize=ft) + plt.show() + + +def fig6(desobj, theta0, rep0, Xpl, Ypl, pg, ng, ax, figset={}, fig={}, exp=True): + + th, reps = desobj.xt[:, 1:3], desobj.reps + + xtick, ytick, cbarf = ( + figset.get("xtick", True), + figset.get("ytick", True), + figset.get("cbar", True), + ) + + ft = 12 + nm = Xpl.shape[0] + cs = ax.contourf(Xpl, Ypl, ng.reshape(nm, nm), cmap=yellow_cmap, alpha=0.75) + cp = ax.contour(Xpl, Ypl, pg.reshape(nm, nm), cmap="coolwarm") + + if cbarf: + cbar = fig.colorbar(cs, ax=ax, pad=0.1) + + for label, x_count, y_count in zip(reps, th[:, 0], th[:, 1]): + + if exp: + if np.any(np.all(theta0 == np.array([x_count, y_count]), axis=1)): + col = "cyan" + else: + col = "blue" + else: + if label > rep0: + col = "blue" + else: + col = "cyan" + + ax.annotate( + label, + xy=(x_count, y_count), + xytext=(0, 0), + textcoords="offset points", + fontsize=ft, + color=col, + ) + + if xtick: + ax.set_xticks([0, 0.5, 1]) + ax.set_xlabel(r"$\vartheta_1$", fontsize=ft) + else: + ax.set_xticks([]) + + if ytick: + ax.set_yticks([0, 0.5, 1]) + ax.set_ylabel(r"$\vartheta_2$", fontsize=ft) + else: + ax.set_yticks([]) + + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.tick_params(axis="both", labelsize=ft) + + +def visual_pritam(des_obj, z0u): + ft = 16 + xtu, repu = des_obj.xt, des_obj.reps + for xtid, xt in enumerate(xtu): + # print(xt) + if xt in z0u: + col = "cyan" + else: + col = "blue" + + plt.annotate( + repu[xtid], + xy=(xt[0], xt[1]), + xytext=(0, 0), + textcoords="offset points", + fontsize=ft, + color=col, + weight="bold", + ) + + plt.xlabel(r"$x_1$") + plt.ylabel(r"$x_2$") + plt.show() + + ut = [] + for xtid, xt in enumerate(xtu): + if xt not in z0u: + ut.append(xt[2]) + plt.hist(ut) + plt.show() + + +import os +import dill as pickle + + +def save_output(desing_obj, name, al_func, seedno, path=None, label=None): + + if path is None: + if not os.path.isdir("output"): + os.mkdir("output") + + design_path = ( + "output/" + + name + + "_" + + al_func + + "_seed_" + + str(seedno) + + "_" + + label + + ".pkl" + ) + else: + design_path = ( + path + name + "_" + al_func + "_seed_" + str(seedno) + "_" + label + ".pkl" + ) + + with open(design_path, "wb") as file: + pickle.dump(desing_obj, file) + + +def read_output(path1, name, al_func, seedno, label=None): + + design_path = ( + path1 + + "output/" + + name + + "_" + + al_func + + "_seed_" + + str(seedno) + + "_" + + label + + ".pkl" + ) + with open(design_path, "rb") as file: + design_obj = pickle.load(file) + + return design_obj + + +def create_entry(desobj, fname, method, s, h): + return [ + { + "MSE": he["MSE"], + "MAD": he["MAD"], + "VAR": he["VAR"], + "MSEy": he["MSEy"], + "MADy": he["MADy"], + "MSEn": he["MSEn"], + "MADn": he["MADn"], + "t": he["t"], + "new": he["new"], + "method": method, + "example": fname, + "s": s, + "h": h, + "horizon": he["h"], + } + for he in desobj["H"] + ] + + +def read_data( + rep0=0, + repf=10, + methods=["ivar", "imse", "unif"], + examples=["unimodal", "banana", "bimodal"], + folderpath=None, + label=None, +): + + datalist = [] + for eid, example in enumerate(examples): + for mid, m in enumerate(methods): + for r in range(rep0, repf): + + desobj = read_output(folderpath, example, m, r, label=label[mid]) + entry = create_entry(desobj, example, m, r, label[mid]) + datalist.extend(entry) + + df = pd.DataFrame(datalist) + return df + + +def read_summary_metric(folderpath, examples, methods, ls, rep=(1, 31)): + dfl = [] + for eid, example in enumerate(examples): + for mid, m in enumerate(methods): + for r in range(rep[0], rep[1]): + + desobj = read_output(folderpath, example, m, r, label=ls[mid]) + for heid, he in enumerate(desobj.H): + sm = he["summary_metric"] + sm["r"] = r + sm["method"] = m + sm["h"] = ls[mid] + sm["t"] = heid + dfl.append(sm) + + df = pd.DataFrame(dfl) + return df diff --git a/examples/Example5/utils_sample.py b/examples/Example5/utils_sample.py new file mode 100644 index 0000000..c70b16b --- /dev/null +++ b/examples/Example5/utils_sample.py @@ -0,0 +1,154 @@ +import numpy as np +import scipy.stats as sps +from utils import heatmap, heatmap_pritam +import matplotlib.pyplot as plt +import sys + + +def sample_from_posterior(cls_data, seed): + import emcee + import scipy.stats as sps + from scipy.stats import qmc + + discard = 250 + nsteps = 1000 + thin = 30 + nwalkers = 40 + seed = int(seed) + + def log_probability(ctheta): + if np.any((ctheta < 0) | (ctheta > 1)): + return -np.inf + else: + if cls_data.dx == 1: + feval = np.array( + [cls_data.function(x, *ctheta) for x in cls_data.x] + ).squeeze() + elif cls_data.dx == 2: + feval = np.array( + [cls_data.function(x[0], x[1], *ctheta) for x in cls_data.x] + ).squeeze() + + rnd = sps.multivariate_normal(mean=feval, cov=cls_data.obsvar) + pvar = rnd.pdf(cls_data.real_data) + sys.float_info.epsilon + return np.log(pvar) + + def sample(ndim, nwalkers, seed): + np.random.seed(seed) + sampler = emcee.EnsembleSampler(nwalkers, ndim, log_probability) + + sampling = qmc.LatinHypercube(d=cls_data.p - cls_data.dx, seed=seed) + loc0 = sampling.random(n=nwalkers) + + # sampling = LHS( + # xlimits=cls_data.zlim[cls_data.dx : cls_data.p, :], random_state=seed + # ) + # loc0 = sampling(nwalkers) + + sampler.run_mcmc(initial_state=loc0, nsteps=nsteps, progress=False) + samples = sampler.get_chain(discard=discard, thin=thin, flat=True) + return samples + + return sample(cls_data.dt, nwalkers, seed) + + +def test_data_gen(cls_data, sample=False): + + # test data + nmesh = 50 + xpl = np.linspace(cls_data.zlim[0][0], cls_data.zlim[0][1], nmesh) + ypl = np.linspace(cls_data.zlim[1][0], cls_data.zlim[1][1], nmesh) + Xpl, Ypl = np.meshgrid(xpl, ypl) + tg = np.vstack([Xpl.ravel(), Ypl.ravel()]).T + pg = np.array( + [ + sps.multivariate_normal( + mean=np.array([cls_data.function(x, *t) for x in cls_data.x]), + cov=cls_data.obsvar, + ).pdf(cls_data.real_data) + for t in tg + ] + )[:, None] + + zg = np.column_stack((np.tile(cls_data.x, (tg.shape[0], 1)), tg)) + fg = np.array([cls_data.function(*xt) for xt in zg])[:, None] + ng = np.array([cls_data.noise(*xt) for xt in zg])[:, None] + + if sample: + t_s = sample_from_posterior(cls_data, 1234) + p_s = np.zeros((t_s.shape[0], 1)) + f_s = np.zeros((t_s.shape[0], 1)) + n_s = np.zeros((t_s.shape[0], 1)) + for t_id, t in enumerate(t_s): + f_s[t_id, 0] = np.array( + [cls_data.function(x, t[0], t[1]) for x in cls_data.x] + ) + n_s[t_id, 0] = np.array([cls_data.noise(x, t[0], t[1]) for x in cls_data.x]) + rnd = sps.multivariate_normal(mean=f_s[t_id, 0], cov=cls_data.obsvar) + p_s[t_id, 0] = rnd.pdf(cls_data.real_data) + + p_se = p_s + sys.float_info.epsilon + w_s = (((1 / p_se)) / np.sum((1 / p_se))).flatten() + + # heatmap(Xpl, Ypl, ng, fg, pg, t_s) + + return tg, fg, pg, zg, ng, t_s, p_s, w_s, f_s, n_s, Xpl, Ypl + else: + return tg, fg, pg, zg, ng, Xpl, Ypl + + +def test_data_gen_pri(cls_data, sample=False): + + nt = 100 + tg = np.linspace(cls_data.zlim[2][0], cls_data.zlim[2][1], nt)[:, None] + pg = np.array( + [ + sps.multivariate_normal( + mean=np.array( + [cls_data.function(x[0], x[1], t) for x in cls_data.x] + ).squeeze(), + cov=cls_data.obsvar, + ).pdf(cls_data.real_data) + for t in tg + ] + ) + + heatmap_pritam(cls_data) + + # (ntot, d) + x_tiled = np.tile(cls_data.x, (tg.shape[0], 1)) + # (ntot, p-d) + t_repeated = np.repeat(tg, cls_data.x.shape[0], axis=0) + # (ntot, p) + zg = np.hstack([x_tiled, t_repeated]) + + fg = np.array([cls_data.function(*xt) for xt in zg])[:, None] + ng = np.array([cls_data.noise(*xt) for xt in zg])[:, None] + + if sample: + t_s = sample_from_posterior(cls_data, 1234) + # t_s = np.linspace(0, 1, 900)[:, None] + p_s = np.zeros((t_s.shape[0], 1)) + f_s = np.zeros((t_s.shape[0] * cls_data.x.shape[0], 1)) + n_s = np.zeros((t_s.shape[0] * cls_data.x.shape[0], 1)) + + for t_id, t in enumerate(t_s): + feval = np.array([cls_data.function(x[0], x[1], t) for x in cls_data.x]) + f_s[t_id * 4 : (t_id + 1) * 4, 0] = feval.flatten() + n_s[t_id * 4 : (t_id + 1) * 4, 0] = np.array( + [cls_data.noise(x[0], x[1], t) for x in cls_data.x] + ).flatten() + + rnd = sps.multivariate_normal(mean=feval.flatten(), cov=cls_data.obsvar) + p_s[t_id, 0] = rnd.pdf(cls_data.real_data) + + p_se = p_s + sys.float_info.epsilon + w_s = (((1 / p_se)) / np.sum((1 / p_se))).flatten() + + plt.scatter(tg, pg, alpha=0.5, marker="+", color="blue", s=50) + plt.scatter(t_s, p_s, alpha=0.1, marker="*", color="red", s=10) + plt.show() + + return tg, fg, pg, zg, ng, t_s, p_s, w_s, f_s, n_s + else: + return tg, fg, pg, zg, ng diff --git a/examples/IJOC2024+/Fig1.py b/examples/IJOC2024+/Fig1.py deleted file mode 100644 index a753fe3..0000000 --- a/examples/IJOC2024+/Fig1.py +++ /dev/null @@ -1,95 +0,0 @@ -from PUQ.performance import performanceModel -from PUQ.performanceutils.utils import ( - plot_acc, - plot_acqtime, - plot_endtime, - plot_errorend, -) -import numpy as np -from result_read import get_rep_data -import matplotlib.pyplot as plt - -n = 2048 -w = 2 -b = 1 -rep = 30 -example = "himmelblau_ex" -s = "himmelblau" -label = ["hybrid_ei_c1000/", "hybrid_ei_c100/", "hybrid_ei_c10/", "rnd/"] -labelf = ["hybrid_ei", "hybrid_ei", "hybrid_ei", "rnd"] -path = "/Users/ozgesurer/Desktop/GithubRepos/parallelUQ/" - -acclevel = 0.01 -result = [] -worker = 1 -for mid, m in enumerate(label): - - PM = performanceModel(worker=1, batch=1, n=n, n0=0) - - # Read from existing experimental data - filename = ( - path - + "performanceAnalytics/new_fun_all/new_examples/" - + example - + "/" - + label[mid] - ) - avgae, avgtime = get_rep_data(s, w, b, rep, filename, labelf[mid]) - - # Gen acq and sim time - xt = np.arange(0, len(avgtime)) - xtest = np.arange(0, n) - PM.gen_acqtime(xt, avgtime, xtest, typeGen="regress") - PM.gen_simtime(0.0001, 0.0001, 0, typeSim="normal") - - # Fit a progress curve - minl = np.min(avgae) - maxl = np.max(avgae) - lnew = [(litem - 0) / (maxl - 0) for litem in avgae] - - x_a = np.log(np.arange(1, len(lnew) + 1)) - y_a = np.log(lnew) - xtest_a = np.log(np.arange(1, n + 1)) - PM.gen_curve(x_a, y_a, xtest_a, typeAcc="regress") - PM.acc = np.exp(PM.acc) - - PM.simulate() - PM.summarize() - PM.complete(acclevel) - result.append(PM) - - -lbl = [r"$\mathcal{A}_1$", r"$\mathcal{A}_2$", r"$\mathcal{A}_3$", r"$\mathcal{A}_4$"] -ft = 25 -fig, axes = plt.subplots(1, 3, figsize=(24, 6)) -plot_acc(axes[0], n, acclevel, result, labellist=lbl, logscale=True, fontsize=ft, n0=1) -plot_endtime( - axes[1], - n, - acclevel, - result, - labellist=lbl, - worker=worker, - logscale=True, - fontsize=ft, -) -plot_errorend( - axes[2], - n, - acclevel, - result, - labellist=lbl, - worker=worker, - logscale=True, - fontsize=ft, -) -axes[1].legend( - loc="upper center", - bbox_to_anchor=(0.5, -0.2), - fancybox=True, - shadow=True, - ncol=4, - fontsize=ft, -) -plt.savefig("Figure1.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() diff --git a/examples/IJOC2024+/Fig10.py b/examples/IJOC2024+/Fig10.py deleted file mode 100644 index 39d7b5e..0000000 --- a/examples/IJOC2024+/Fig10.py +++ /dev/null @@ -1,262 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from PUQ.performance import performanceModel -import time -import matplotlib.colors as mcolors -from mpl_toolkits.axes_grid1 import make_axes_locatable -from matplotlib.gridspec import GridSpec - -start = time.time() - -repno = 10 -varlist = [1] -acclevel = 0.2 -n = 2560 -workers = [1, 2, 4, 8, 16, 32, 64, 128, 256] -batches = [1, 2, 4, 8, 16, 32, 64, 128, 256] -s_mean = [2**0, 2**3, 2**6] -accparams = [ - [-1, 0.15], - [-1, 0.18], - [-1, 0.21], - [-1, 0.24], - [-1, 0.27], - [-1, 0.30], - [-1, 0.33], - [-1, 0.36], - [-1, 0.39], -] -am = 2 ** (-1) - -ft = 25 -lw = 5 -ms = 15 -me = 200 - -fig = plt.figure(figsize=(20, 15)) -gs = GridSpec(3, 6, width_ratios=[1, 0.04, 1, 0.04, 1, 0.04]) - -for sid, sm in enumerate(s_mean): - result = [] - for id_b, b in enumerate(batches): - for r in range(repno): - for id_w, w in enumerate(workers): - if b <= w: - PM = performanceModel(worker=w, batch=b, n=n, n0=w) - PM.gen_acqtime(am, am, 2 ** (-6), typeGen="linear") - PM.gen_simtime(sm, sm, 0.001, typeSim="normal", seed=r) - PM.gen_curve( - accparams[id_b][0], accparams[id_b][1], typeAcc="exponential" - ) - PM.simulate() - # PM.summarize() - PM.complete(acclevel) - result.append( - { - "r": r, - "b": b, - "var": 1, - "w": w, - "sm": sm, - "res": PM, - } - ) - - idle = np.zeros((len(batches), len(workers))) - computing = np.zeros((len(batches), len(workers))) - endtime = np.zeros((len(batches), len(workers))) - endtime_temp = np.zeros((len(batches), len(workers))) - for wid, w in enumerate(workers): - for bid, b in enumerate(batches): - if b <= w: - res_c = [res for res in result if ((res["w"] == w) & (res["b"] == b))] - idle[bid, wid] = np.mean( - [res_c[i]["res"].avg_idle_time for i in range(0, repno)] - ) - computing[bid, wid] = np.mean( - [res_c[i]["res"].computing_hours for i in range(0, repno)] - ) - endtime[bid, wid] = np.mean( - [res_c[i]["res"].complete_time for i in range(0, repno)] - ) - endtime_temp[bid, wid] = np.mean( - [res_c[i]["res"].complete_time for i in range(0, repno)] - ) - else: - idle[bid, wid] = np.nan - computing[bid, wid] = np.nan - endtime[bid, wid] = np.nan - endtime_temp[bid, wid] = np.nan - - for bid, b in enumerate(batches): - for wid, w in enumerate(workers): - if b <= w: - endtime[bid, wid] = endtime[bid, wid] / endtime_temp[bid, bid] - - bidle, bcomph, bend = ( - np.zeros(len(workers)), - np.zeros(len(workers)), - np.zeros(len(workers)), - ) - for wid, w in enumerate(workers): - bidle[wid] = np.nanargmin(idle[:, wid]) - bcomph[wid] = np.nanargmin(computing[:, wid]) - bend[wid] = np.nanargmin(endtime_temp[:, wid]) - - widle, wcomph, wend = ( - np.zeros(len(workers)), - np.zeros(len(workers)), - np.zeros(len(workers)), - ) - for bid, b in enumerate(batches): - widle[bid] = np.nanargmin(idle[bid, :]) - wcomph[bid] = np.nanargmin(computing[bid, :]) - wend[bid] = np.nanargmin(endtime_temp[bid, :]) - - if sid == 0: - colid = 0 - elif sid == 1: - colid = 2 - elif sid == 2: - colid = 4 - - # AXIS 1 - subplot_ax = fig.add_subplot(gs[1, colid]) - masked_data = np.ma.masked_invalid(idle) - norm = mcolors.LogNorm(vmin=np.nanmin(idle), vmax=np.nanmax(idle)) - im = subplot_ax.imshow(masked_data, norm=norm, cmap="YlOrRd") - - cbar_ax = fig.add_subplot(gs[1, colid + 1]) - cbar = fig.colorbar(im, cax=cbar_ax) - cbar.ax.tick_params(labelsize=ft - 8) - cbar.ax.yaxis.set_ticks_position("left") - cbar.ax.yaxis.set_tick_params(width=2, color="black") - - if sid == 2: - cbar.ax.set_ylabel("Idle time", rotation=-90, va="bottom", fontsize=ft) - - # # Show all ticks and label them with the respective list entries - subplot_ax.set_yticks(np.arange(len(batches)), labels=batches) - subplot_ax.set_xticks(np.arange(len(workers)), labels=workers) - subplot_ax.tick_params(axis="both", which="major", labelsize=ft - 5) - plt.setp( - subplot_ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor" - ) - if sid == 0: - subplot_ax.set_ylabel(r"Batch size ($b$)", fontsize=ft) - - # AXIS 2 - subplot_ax = fig.add_subplot(gs[2, colid]) - masked_data = np.ma.masked_invalid(computing) - norm = mcolors.LogNorm(vmin=np.nanmin(computing), vmax=np.nanmax(computing)) - im = subplot_ax.imshow(masked_data, norm=norm, cmap="YlOrRd") - cbar_ax = fig.add_subplot(gs[2, colid + 1]) - cbar = fig.colorbar(im, cax=cbar_ax) - cbar.ax.tick_params(labelsize=ft - 5) - if sid == 2: - cbar.ax.set_ylabel("Computing hours", rotation=-90, va="bottom", fontsize=ft) - # Adjust ticks to be on the left side - cbar.ax.yaxis.set_ticks_position("left") - cbar.ax.yaxis.set_tick_params(width=2, color="black") - # # Show all ticks and label them with the respective list entries - subplot_ax.set_yticks(np.arange(len(batches)), labels=batches) - subplot_ax.set_xticks(np.arange(len(workers)), labels=workers) - if sid == 0: - subplot_ax.set_ylabel(r"Batch size ($b$)", fontsize=ft) - subplot_ax.tick_params(axis="both", which="major", labelsize=ft - 5) - subplot_ax.set_xlabel(r"# of workers ($w$)", fontsize=ft) - plt.setp( - subplot_ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor" - ) - # AXIS 0 - subplot_ax = fig.add_subplot(gs[0, colid]) - masked_data = np.ma.masked_invalid(endtime) - - bounds = [ - 0.0, - 2 ** (-9), - 2 ** (-8), - 2 ** (-7), - 2 ** (-6), - 2 ** (-5), - 2 ** (-4), - 2 ** (-3), - 2 ** (-2), - 2 ** (-1), - 2**0, - ] # bounds to differentiate colors - cmap = plt.get_cmap("YlOrRd", 11) - norm = mcolors.BoundaryNorm(bounds, cmap.N) # Normalize colors based on bounds - - cbar_ax = fig.add_subplot(gs[0, colid + 1]) - im = subplot_ax.imshow(masked_data, cmap=cmap, norm=norm) - cbar = fig.colorbar(im, cax=cbar_ax, orientation="vertical") - cbar.ax.tick_params(labelsize=ft - 5) - cbar.ax.yaxis.set_ticks_position("left") - cbar.ax.yaxis.set_tick_params(width=2, color="black") - cbar.set_ticks( - [ - 0.0, - 2 ** (-9), - 2 ** (-8), - 2 ** (-7), - 2 ** (-6), - 2 ** (-5), - 2 ** (-4), - 2 ** (-3), - 2 ** (-2), - 2 ** (-1), - 2**0, - ] - ) - cbar.set_ticklabels( - [ - r"$0$", - r"$2^{-9}$", - r"$2^{-8}$", - r"$2^{-7}$", - r"$2^{-6}$", - r"$2^{-5}$", - r"$2^{-4}$", - r"$2^{-3}$", - r"$2^{-2}$", - r"$2^{-1}$", - r"$2^{0}$", - ] - ) - if sid == 2: - cbar.ax.set_ylabel( - "Relative wall-clock time", rotation=-90, va="bottom", fontsize=ft - ) - - # # Show all ticks and label them with the respective list entries - subplot_ax.set_yticks(np.arange(len(batches)), labels=batches) - subplot_ax.set_xticks(np.arange(len(workers)), labels=workers) - if sid == 0: - subplot_ax.set_ylabel(r"Batch size ($b$)", fontsize=ft) - subplot_ax.tick_params(axis="both", which="major", labelsize=ft - 5) - plt.setp( - subplot_ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor" - ) - - if sid == 0: - if colid == 0: - subplot_ax.set_title(r"$\breve{a}^S = 2^0$", fontsize=ft) - if sid == 1: - if colid == 2: - subplot_ax.set_title(r"$\breve{a}^S = 2^3$", fontsize=ft) - if sid == 2: - if colid == 4: - subplot_ax.set_title(r"$\breve{a}^S = 2^6$", fontsize=ft) - # for i in range(len(batches)): - # for j in range(len(workers)): - # if batches[i] <= workers[j]: - # text = ax[0, sid].text(j, i, np.round(endtime[i, j], 1), - # ha="center", va="center", color="black", fontsize=ft-10) - - -fig.suptitle("Simulation time increases \u2192", fontsize=ft) -fig.subplots_adjust(top=0.9, bottom=0.1) -# fig.tight_layout() -plt.savefig("Figure10.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() diff --git a/examples/IJOC2024+/Fig2.py b/examples/IJOC2024+/Fig2.py deleted file mode 100644 index a0af7f8..0000000 --- a/examples/IJOC2024+/Fig2.py +++ /dev/null @@ -1,133 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments -import scipy.stats as sps -from PUQ.prior import prior_dist -from test_funcs import himmelblau -import matplotlib.pyplot as plt -import time - -if __name__ == "__main__": - start = time.time() - - args = parse_arguments() - - example = "himmelblau" - cls_data = eval(example)() - - # # # Create a mesh for test set # # # - xpl = np.linspace(cls_data.thetalimits[0][0], cls_data.thetalimits[0][1], 50) - ypl = np.linspace(cls_data.thetalimits[1][0], cls_data.thetalimits[1][1], 50) - Xpl, Ypl = np.meshgrid(xpl, ypl) - th = np.vstack([Xpl.ravel(), Ypl.ravel()]) - setattr(cls_data, "theta", th.T) - - ftest = np.zeros(2500) - for tid, t in enumerate(th.T): - ftest[tid] = cls_data.function(t[0], t[1]) - thetatest = th.T - ptest = np.zeros(thetatest.shape[0]) - for i in range(ftest.shape[0]): - mean = ftest[i] - rnd = sps.multivariate_normal(mean=mean, cov=cls_data.obsvar) - ptest[i] = rnd.pdf(cls_data.real_data) - - test_data = {"theta": thetatest, "f": ftest, "p": ptest, "p_prior": 1} - - # # # # # # # # # # # # # # # # # # # # # - prior_func = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - # # # # # # # # # # # # # # # # # # # # # - - init_seeds = 2 - final_seeds = 3 - - n_init = 10 - for s in np.arange(init_seeds, final_seeds): - - thetainit = prior_func.rnd(n_init, s) - finit = np.zeros(n_init) - for tid, t in enumerate(thetainit): - finit[tid] = cls_data.function(t[0], t[1]) - test_data["thetainit"] = thetainit - test_data["finit"] = finit[None, :] - - al_data_ei = designer( - data_cls=cls_data, - method="SEQCALOPT", - args={ - "mini_batch": 1, - "nworkers": 2, - "AL": "ei", - "seed_n0": int(s), - "prior": prior_func, - "data_test": test_data, - "max_evals": 200, - "candsize": args.candsize, - "refsize": args.refsize, - "believer": args.believer, - }, - ) - - al_data_hyb = designer( - data_cls=cls_data, - method="SEQCALOPT", - args={ - "mini_batch": 1, - "nworkers": 2, - "AL": "hybrid_ei", - "seed_n0": int(s), - "prior": prior_func, - "data_test": test_data, - "max_evals": 200, - "candsize": args.candsize, - "refsize": args.refsize, - "believer": args.believer, - }, - ) - - show = True - ft = 20 - ms = 50 - if show: - theta_ei = al_data_ei._info["theta"] - theta_hyb = al_data_hyb._info["theta"] - - fig, ax = plt.subplots(1, 2, figsize=(16, 6)) - cp = ax[0].contour(Xpl, Ypl, ptest.reshape(50, 50), 20, cmap="RdGy") - ax[0].scatter( - theta_ei[:, 0], theta_ei[:, 1], c="black", marker="+", s=ms, zorder=2 - ) - ax[0].scatter( - thetainit[:, 0], - thetainit[:, 1], - zorder=2, - marker="o", - facecolors="none", - edgecolors="blue", - ) - ax[0].set_xlabel(r"$\theta_1$", fontsize=ft) - ax[0].set_ylabel(r"$\theta_2$", fontsize=ft) - ax[0].tick_params(axis="both", labelsize=ft) - - cp = ax[1].contour(Xpl, Ypl, ptest.reshape(50, 50), 20, cmap="RdGy") - ax[1].scatter( - theta_hyb[:, 0], theta_hyb[:, 1], c="black", marker="+", s=ms, zorder=2 - ) - ax[1].scatter( - thetainit[:, 0], - thetainit[:, 1], - zorder=2, - marker="o", - facecolors="none", - edgecolors="blue", - ) - ax[1].set_xlabel(r"$\theta_1$", fontsize=ft) - ax[1].set_ylabel(r"$\theta_2$", fontsize=ft) - ax[1].tick_params(axis="both", labelsize=ft) - plt.savefig("Figure2.jpg", format="jpeg", bbox_inches="tight", dpi=500) - plt.show() - - end = time.time() - print("Elapsed time =", round(end - start, 3)) diff --git a/examples/IJOC2024+/Fig3.py b/examples/IJOC2024+/Fig3.py deleted file mode 100644 index b7bb857..0000000 --- a/examples/IJOC2024+/Fig3.py +++ /dev/null @@ -1,176 +0,0 @@ -import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec -import numpy as np -from PUQ.performance import performanceModel -import matplotlib.patches as patches - -# Create a figure -fig = plt.figure(figsize=(24, 6)) - -# Define a GridSpec layout -gs = gridspec.GridSpec(1, 3) # 1 row, 3 columns - -# Create subplots with varying spaces between them by adjusting the positions -ax1 = fig.add_subplot(gs[0]) # First subplot -ax2 = fig.add_subplot(gs[1]) # Second subplot -ax3 = fig.add_subplot(gs[2]) # Third subplot - -# Adjust positions manually for different spacing -ax1.set_position([0.1, 0.1, 0.25, 0.8]) # [left, bottom, width, height] -ax2.set_position([0.43, 0.1, 0.25, 0.8]) # Less width, more space on the left -ax3.set_position([0.76, 0.1, 0.25, 0.8]) # Normal width, more space between ax2 and ax3 - -# Plot data on the subplots -acclevel = [0.1, 0.2, 0.25] -n = 1280 -ft = 25 -worker = 128 -n0 = 0 -batches = [1, 64, 128] -clist = ["b", "r", "g", "m", "y", "c"] -mlist = ["P", "o", "*", "s", "p", "h"] -linelist = ["-", "--", "-.", ":", "-.", ":"] -level = 0.3 -for bid, b in enumerate(batches): - PM = performanceModel(worker=worker, batch=b, n=n, n0=n0) - PM.gen_curve(-1, acclevel[bid], typeAcc="exponential") - PM.gen_acqtime(1, 1, 0.25, typeGen="linear") - PM.gen_simtime(1, 1, 0.1, typeSim="normal", seed=1) - PM.simulate() - # PM.summarize() - - PM.complete(level) - - ax1.plot(PM.acc[0:500], linestyle=linelist[bid], linewidth=5.0, color=clist[bid]) - - ax1.hlines(y=level, xmin=0, xmax=500, linewidth=5, color="k") - - ax1.vlines( - x=PM.complete_no, - ymin=0, - ymax=level, - linewidth=5, - color=clist[bid], - linestyles=(0, (2, 5)), - ) - -# Annotating outside of ax1 using figure-relative coordinates -ax1.annotate( - r"$\alpha$", - xy=(0.3, 0.34), - xycoords="figure fraction", - fontsize=ft, - fontweight="bold", - color="black", -) - -# # Define arrow properties -# arrow = patches.FancyArrowPatch( -# (0.35, 0.35), # Start point in figure coordinates -# (0.38, 0.35), # End point in figure coordinates -# mutation_scale=50, # Size of the arrow -# arrowstyle="->", # Arrow style -# color="black", -# linewidth=5, -# transform=fig.transFigure, # Use figure coordinates -# ) - -# # Add the arrow to the figure -# fig.patches.append(arrow) - -ax1.set_xlabel("# of parameters", fontsize=ft) -ax1.set_ylabel("Error", fontsize=ft) -ax1.tick_params(axis="both", which="major", labelsize=ft - 5) -ax1.set_xlim(0, 500) -ax1.set_ylim(0, 1.1) - -# AXIS 2 -# Generate random data -xmax = 25 -np.random.seed(1) -means = [5, 10, 15, 20] - -datas = [] -for m in means: - rnddat = np.random.normal(m, 2, 30) - rnddat = [0.1 if r < 0 else r for r in rnddat] - datas.append(rnddat) - -# Set the number of bins -bins = np.linspace(-2, xmax, 30) - -# Plot histograms horizontally with vertical shifts -for i, data in enumerate(datas): - counts, _ = np.histogram(data, bins=bins) - # Plot histogram as a horizontal bar plot - ax2.barh( - bins[:-1], - counts, - height=0.8, - color="red", - alpha=0.7, - label=i, - edgecolor="none", - align="center", - left=i * 10, - ) - -# Add labels and title -ax2.set_xlabel("t", fontsize=ft) -ax2.set_ylabel("Acquisition time", fontsize=ft) - -# Set custom x-axis ticks and labels -tick_positions = [0, 10, 20, 30] -tick_labels = ["0", "1", "2", "3"] - -ax2.set_xticks(tick_positions) -ax2.set_xticklabels(tick_labels) -ax2.plot([0, 10, 20, 30], [5, 10, 15, 20], color="black", linestyle="--", linewidth=5) -ax2.scatter([0, 10, 20, 30], [5, 10, 15, 20], color="black", marker="*", s=1000) -ax2.tick_params(axis="both", which="major", labelsize=ft - 5) -ax2.set_ylim(0, 25) - -# AXIS 3 -ax3.hist( - np.random.normal(5, 2, 100), bins=30, edgecolor="white", color="blue", alpha=0.5 -) -ax3.set_xlabel("Simulation time", fontsize=ft) -ax3.set_ylabel("Frequency", fontsize=ft) -ax3.tick_params(axis="both", which="major", labelsize=ft - 5) -# ax3.vlines(x=5, ymin=0, ymax=9, color="black", linestyle="--", linewidth=5) - -# Set titles to distinguish the plots -ax1.set_title(r"Find $n_k(b, \alpha)$ for worker size $w$", fontsize=ft) -ax2.set_title("Histogram of $a_{\omega,k}(b,t)$", fontsize=ft) -ax3.set_title("Histogram of $s_{\omega,j}$", fontsize=ft) -ax3.set_xlim(0, 11) -# plt.annotate( -# "1", -# (0.05, 1), -# xycoords="figure fraction", -# bbox={"boxstyle": "circle", "color": "lightgrey"}, -# fontsize=ft, -# color="black", -# ) - -# plt.annotate( -# "2", -# (0.4, 1), -# xycoords="figure fraction", -# bbox={"boxstyle": "circle", "color": "lightgrey"}, -# fontsize=ft, -# color="black", -# ) - -# plt.annotate( -# "3", -# (0.7, 1), -# xycoords="figure fraction", -# bbox={"boxstyle": "circle", "color": "lightgrey"}, -# fontsize=ft, -# color="black", -# ) - -plt.gca().set_aspect("equal", adjustable="box") -plt.savefig("Figure3.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() diff --git a/examples/IJOC2024+/Fig4_5_12_13.py b/examples/IJOC2024+/Fig4_5_12_13.py deleted file mode 100644 index a553e46..0000000 --- a/examples/IJOC2024+/Fig4_5_12_13.py +++ /dev/null @@ -1,85 +0,0 @@ -from plotutils import plotresult, plotparams -import matplotlib.pyplot as plt -import numpy as np - -# Generates Fig 3, 4, 11, 12 -clist = ["b", "r", "g", "m", "y", "c"] -mlist = ["P", "o", "*", "s", "p", "h"] -linelist = ["-", "--", "-.", ":", "-.", ":"] - -lw = 5 -ms = 15 -me = 200 - -labelsb = ["EI", "EIVAR", "HYBRID", "RND"] -method = ["ei", "eivar", "hybrid_ei", "rnd"] -batch = 1 -worker = 2 -rep = 30 -fonts = 25 - -path = "/Users/ozgesurer/Desktop/sh_files/" -figs = [["himmelblau", "holder", "easom"], ["sphere", "matyas", "ackley"]] -for ex_id, example_name in enumerate(figs): - for metric in ["AE", "MAD"]: - fig, axes = plt.subplots(1, 3, figsize=(24, 6)) - for exid, ex in enumerate(example_name): - for mid, m in enumerate(method): - out = ex + "_" + m - avgAE, avgtime, avgTV = plotresult( - path, out, ex, worker, batch, rep, m, n0=0, nf=1000 - ) - # print(avgAE[0:10]) - if metric == "AE": - axes[exid].plot( - np.arange(len(avgAE)), - avgAE, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - linewidth=lw, - marker=mlist[mid], - markersize=ms, - markevery=me, - ) - else: - axes[exid].plot( - np.arange(len(avgTV)), - avgTV, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - linewidth=lw, - marker=mlist[mid], - markersize=ms, - markevery=me, - ) - axes[exid].set_yscale("log") - axes[exid].set_xlabel("# of parameters", fontsize=fonts) - if exid == 0: - if metric == "AE": - axes[exid].set_ylabel(r"$\delta$", fontsize=fonts) - else: - axes[exid].set_ylabel(r"MAD", fontsize=fonts) - axes[exid].tick_params(axis="both", which="major", labelsize=fonts - 5) - - axes[1].legend( - loc="upper center", - bbox_to_anchor=(0.5, -0.2), - fancybox=True, - shadow=True, - ncol=4, - fontsize=fonts, - ) - if ex_id == 0: - if metric == "AE": - plt.savefig("Figure4.jpg", format="jpeg", bbox_inches="tight", dpi=500) - else: - plt.savefig("Figure5.jpg", format="jpeg", bbox_inches="tight", dpi=500) - else: - if metric == "AE": - plt.savefig("Figure12.jpg", format="jpeg", bbox_inches="tight", dpi=500) - else: - plt.savefig("Figure13.jpg", format="jpeg", bbox_inches="tight", dpi=500) - - plt.show() diff --git a/examples/IJOC2024+/Fig6_14.py b/examples/IJOC2024+/Fig6_14.py deleted file mode 100644 index f3f8fd2..0000000 --- a/examples/IJOC2024+/Fig6_14.py +++ /dev/null @@ -1,78 +0,0 @@ -from plotutils import plotresult, plotparams -import matplotlib.pyplot as plt -import numpy as np - - -clist = ["b", "r", "g", "m", "y", "c"] -mlist = ["P", "o", "*", "s", "p", "h"] -linelist = ["-", "--", "-.", ":", "-.", ":"] - - -labelsb = ["b=1", "b=5", "b=25", "b=125"] -method = ["hybrid_ei"] -batch_sizes = [1, 5, 25, 125] -worker = 126 -rep = 30 -fonts = 25 -nf = 1000 -lw = 5 -ms = 15 -me = 200 - -path = "/Users/ozgesurer/Desktop/batch_mode/" -figs = [["sphere", "matyas", "ackley"], ["himmelblau", "holder", "easom"]] -for figno, example_name in enumerate(figs): - for metric in ["AE"]: - fig, axes = plt.subplots(1, 3, figsize=(24, 6)) - for exid, ex in enumerate(example_name): - for bid, b in enumerate(batch_sizes): - for mid, m in enumerate(method): - out = ex + "_" + m + "_" + "b" + str(b) + "_" + "w125" - avgAE, avgtime, avgTV = plotresult( - path, out, ex, worker, b, rep, m, n0=123, nf=nf - ) - if metric == "AE": - axes[exid].plot( - np.arange(len(avgAE)), - avgAE, - label=labelsb[bid], - color=clist[bid], - linestyle=linelist[bid], - linewidth=lw, - marker=mlist[bid], - markersize=ms, - markevery=me, - ) - else: - axes[exid].plot( - np.arange(len(avgTV)), - avgTV, - label=labelsb[bid], - color=clist[bid], - linestyle=linelist[bid], - linewidth=lw, - marker=mlist[bid], - markersize=ms, - markevery=me, - ) - axes[exid].set_yscale("log") - axes[exid].set_xlabel("# of parameters", fontsize=fonts) - if exid == 0: - if metric == "AE": - axes[exid].set_ylabel(r"$\delta$", fontsize=fonts) - else: - axes[exid].set_ylabel(r"MAD", fontsize=fonts) - axes[exid].tick_params(axis="both", which="major", labelsize=fonts - 5) - axes[1].legend( - loc="upper center", - bbox_to_anchor=(0.5, -0.2), - fancybox=True, - shadow=True, - ncol=4, - fontsize=fonts, - ) - if figno == 0: - plt.savefig("Figure6.jpg", format="jpeg", bbox_inches="tight", dpi=500) - else: - plt.savefig("Figure14.jpg", format="jpeg", bbox_inches="tight", dpi=500) - plt.show() diff --git a/examples/IJOC2024+/Fig7.py b/examples/IJOC2024+/Fig7.py deleted file mode 100644 index 9ce9731..0000000 --- a/examples/IJOC2024+/Fig7.py +++ /dev/null @@ -1,53 +0,0 @@ -from PUQ.performance import performanceModel -from PUQ.performanceutils.utils import ( - plot_acc, - plot_endtime, - plot_errorend, -) -import matplotlib.pyplot as plt -import time - -start = time.time() -scale_list = [1, 1.1, 1.2] -acclevel = [0.1, 0.2, 0.25] -result = [] -n = 1280 -ft = 25 -worker = 128 -n0 = 0 -batches = [1, 64, 128] -level = 0.1 -for bid, b in enumerate(batches): - PM = performanceModel(worker=worker, batch=b, n=n, n0=n0) - PM.gen_acqtime(1, 1, 0.25, typeGen="linear") - PM.gen_simtime(1, 1, 0.1, typeSim="normal", seed=1) - PM.gen_curve(-1, acclevel[bid], typeAcc="exponential") - - PM.simulate() - # PM.summarize() - - PM.complete(level) - result.append(PM) - -labs = ["$b=1$", "$b=64$", "$b=128$"] -fig, axes = plt.subplots(1, 3, figsize=(24, 6)) -plot_acc(axes[0], n, level, result, labellist=labs, logscale=False, fontsize=ft, n0=n0) -plot_endtime( - axes[1], n, level, result, labellist=labs, worker=worker, logscale=True, fontsize=ft -) -plot_errorend( - axes[2], n, level, result, labellist=labs, worker=worker, logscale=True, fontsize=ft -) -axes[1].legend( - loc="upper center", - bbox_to_anchor=(0.5, -0.2), - fancybox=True, - shadow=True, - ncol=4, - fontsize=ft, -) -plt.savefig("Figure7.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() - -end = time.time() -print("Elapsed time =", round(end - start, 3)) diff --git a/examples/IJOC2024+/Fig8.py b/examples/IJOC2024+/Fig8.py deleted file mode 100644 index 96a1edb..0000000 --- a/examples/IJOC2024+/Fig8.py +++ /dev/null @@ -1,134 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from PUQ.performance import performanceModel -import time -from matplotlib.ticker import FixedFormatter -from matplotlib.gridspec import GridSpec - -start = time.time() - -repno = 10 -n = 2560 -varlist = [0.1, 10] - -worker = 256 -batches = [1, 2, 4, 8, 16, 32, 64, 128, 256] -s_mean = [2**3, 2**6, 2**9] -a_mean = [2 ** (-2), 2 ** (-1), 2**0, 2**1, 2**2, 2**3, 2**4, 2**5] -a_tick = [ - r"$2^{-2}$", - r"$2^{-1}$", - r"$2^0$", - r"$2^1$", - r"$2^2$", - r"$2^3$", - r"$2^4$", - r"$2^5$", -] -accparams = [ - [-1, 0.15], - [-1, 0.18], - [-1, 0.21], - [-1, 0.24], - [-1, 0.27], - [-1, 0.30], - [-1, 0.33], - [-1, 0.36], - [-1, 0.39], -] -acclevel = 0.2 -ft = 25 - - -fig = plt.figure(figsize=(20, 10)) -gs = GridSpec(2, 4, width_ratios=[1, 1, 1, 0.05]) - -for vid, var in enumerate(varlist): - for sid, sm in enumerate(s_mean): - result = [] - for aid, am in enumerate(a_mean): - for r in range(repno): - for id_b, b in enumerate(batches): - PM = performanceModel(worker=worker, batch=b, n=n, n0=worker) - PM.gen_acqtime(am, am, 0.001, typeGen="linear") - PM.gen_simtime(sm, sm * var, 0.01, typeSim="normal", seed=r) - PM.gen_curve(-1, accparams[id_b][1], typeAcc="exponential") - PM.simulate() - # PM.summarize() - PM.complete(acclevel) - result.append( - { - "r": r, - "b": b, - "am": am, - "var": var, - "sm": sm, - "res": PM, - } - ) - # print(PM.complete_no) - - timemat = np.zeros((len(a_mean), len(batches))) - for aid, am in enumerate(a_mean): - for bid, b in enumerate(batches): - res_c = [res for res in result if ((res["am"] == am) & (res["b"] == b))] - timemat[aid, bid] = np.mean( - [res_c[i]["res"].complete_time for i in range(0, len(res_c))] - ) - - subplot_ax = fig.add_subplot(gs[vid, sid]) - bo = np.argsort(np.argsort(timemat, axis=1), axis=1) - im = subplot_ax.imshow(bo, aspect="auto", cmap="YlOrRd") - if sid == 2: - cbar_ax = fig.add_subplot(gs[vid, 3]) - cbar = fig.colorbar( - im, - cax=cbar_ax, - ticks=np.array([0.0, 0.5, 1.0]) * bo.max(), - format=FixedFormatter(["lowest", "middle", "highest"]), - ) - cbar.ax.tick_params(labelsize=ft - 5) - cbar.ax.set_ylabel( - "Wall-clock time", rotation=-90, va="bottom", fontsize=ft - ) - - # # Show all ticks and label them with the respective list entries - subplot_ax.set_xticks(np.arange(len(batches)), labels=batches) - subplot_ax.set_yticks(np.arange(len(a_mean)), labels=a_tick) - - # Rotate the tick labels and set their alignment. - plt.setp( - subplot_ax.get_xticklabels(), - rotation=45, - ha="right", - rotation_mode="anchor", - ) - - if vid == 1: - subplot_ax.set_xlabel(r"Batch size ($b$)", fontsize=ft) - if sid == 0: - subplot_ax.set_ylabel(r"Acquisition time ($\breve{a}^A$)", fontsize=ft) - - subplot_ax.tick_params(axis="both", which="major", labelsize=ft - 5) - - if vid == 0: - if sid == 0: - subplot_ax.set_title(r"$\breve{a}^S = 2^3$", fontsize=ft) - elif sid == 1: - subplot_ax.set_title(r"$\breve{a}^S = 2^6$", fontsize=ft) - elif sid == 2: - subplot_ax.set_title(r"$\breve{a}^S = 2^9$", fontsize=ft) - - # for aid, am in enumerate(a_mean): - # for bid, b in enumerate(batches): - # text = ax[vid, sid].text(bid, aid, np.round(timemat[aid, bid]/np.min(timemat), 1), - # ha="center", va="center", color="black", fontsize=ft-10) - - -fig.suptitle("Simulation time increases \u2192", fontsize=ft) -plt.savefig("Figure8.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() - - -end = time.time() -print("Elapsed time =", round(end - start, 3)) diff --git a/examples/IJOC2024+/Fig9.py b/examples/IJOC2024+/Fig9.py deleted file mode 100644 index bf9a4cb..0000000 --- a/examples/IJOC2024+/Fig9.py +++ /dev/null @@ -1,131 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from PUQ.performance import performanceModel -from mpl_toolkits.axes_grid1 import make_axes_locatable -import matplotlib.colors as mcolors -from matplotlib.gridspec import GridSpec - -repno = 10 -n = 2560 -worker = 256 -batches = [1, 2, 4, 8, 16, 32, 64, 128, 256] -s_mean = [2**0, 2**1, 2**2, 2**3, 2**4, 2**5] -a_mean = [2 ** (-3), 2 ** (-2), 2 ** (-1), 2**0, 2**1, 2**2] -sim_tick = [r"$2^0$", r"$2^1$", r"$2^2$", r"$2^3$", r"$2^4$", r"$2^5$"] -acq_tick = [r"$2^{-3}$", r"$2^{-2}$", r"$2^{-1}$", r"$2^{0}$", r"$2^1$", r"$2^2$"] -acclevel = 0.2 -accparams = [[-1, 0.1], [-1, 0.3], [-1, 0.5]] - -fig = plt.figure(figsize=(20, 10)) -gs = GridSpec(1, 4, width_ratios=[1, 1, 1, 0.05]) - -for accid, acc in enumerate(accparams): - result = [] - for aid, am in enumerate(a_mean): - for sid, sm in enumerate(s_mean): - for r in range(repno): - for id_b, b in enumerate(batches): - PM = performanceModel(worker=worker, batch=b, n=n, n0=worker) - PM.gen_acqtime(am, am, 2 ** (-5), typeGen="linear") - PM.gen_simtime(sm, sm, 0.01, typeSim="normal", seed=r) - PM.gen_curve(acc[0], acc[1] + id_b * 0.02, typeAcc="exponential") - PM.simulate() - # PM.summarize() - PM.complete(acclevel) - result.append( - { - "r": r, - "b": b, - "am": am, - "var": 1, - "sm": sm, - "res": PM, - } - ) - # print(PM.complete_no) - - timemat = np.zeros((len(a_mean), len(s_mean))) - bmat = np.zeros((len(a_mean), len(s_mean))) - for aid, am in enumerate(a_mean): - for sid, sm in enumerate(s_mean): - res_c = [res for res in result if ((res["am"] == am) & (res["sm"] == sm))] - - rmin = np.inf - bmin = np.inf - for bid, b in enumerate(batches): - cs = [rs["res"].complete_time for rs in res_c if rs["b"] == b] - if np.mean(cs) < rmin: - rmin = np.mean(cs) - bmin = b - - timemat[aid, sid] = rmin - bmat[aid, sid] = bmin - - ft = 25 - subplot_ax = fig.add_subplot(gs[accid]) - # Show all ticks and label them with the respective list entries - subplot_ax.set_xticks(np.arange(len(s_mean)), labels=sim_tick) - subplot_ax.set_yticks(np.arange(len(a_mean)), labels=acq_tick) - subplot_ax.tick_params(axis="both", which="major", labelsize=ft - 5) - # Rotate the tick labels and set their alignment. - plt.setp( - subplot_ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor" - ) - - # Loop over data dimensions and create text annotations. - for i in range(len(a_mean)): - for j in range(len(s_mean)): - if bmat[i, j] >= 64: - text = subplot_ax.text( - j, - i, - int(timemat[i, j]), - ha="center", - va="center", - color="white", - fontsize=ft - 5, - ) - else: - text = subplot_ax.text( - j, - i, - int(timemat[i, j]), - ha="center", - va="center", - color="black", - fontsize=ft - 5, - ) - - subplot_ax.grid(which="minor", color="w", linestyle="-", linewidth=3) - subplot_ax.set_xlabel(r"Simulation time ($\breve{a}^S$)", fontsize=ft) - if accid == 0: - subplot_ax.set_ylabel(r"Acquisition time ($\breve{a}^A$)", fontsize=ft) - - # bounds to differentiate colors - bounds = [0.5, 1.5, 3.5, 7.5, 15.5, 31.5, 63.5, 127.5, 255.5, 512] - cmap = plt.get_cmap("YlOrRd", len(batches)) - # Normalize colors based on bounds - norm = mcolors.BoundaryNorm(bounds, cmap.N) - im = subplot_ax.imshow(bmat, cmap=cmap, norm=norm) - - if accid == 2: - cbar_ax = fig.add_subplot(gs[3]) - - # Heatmap - # divider = make_axes_locatable(cbar_ax) - # cax = divider.append_axes('right', size='5%', pad=0.2) - cbar = fig.colorbar(im, cax=cbar_ax, orientation="vertical") - cbar_ax.set_aspect(20) - cbar.ax.tick_params(labelsize=ft - 5) - cbar.set_ticks(batches) - cbar.ax.set_ylabel(r"Batch size ($b$)", rotation=-90, va="bottom", fontsize=ft) - - if accid == 0: - subplot_ax.set_title(r"$\mathcal{A}_1$", fontsize=ft) - elif accid == 1: - subplot_ax.set_title(r"$\mathcal{A}_2$", fontsize=ft) - elif accid == 2: - subplot_ax.set_title(r"$\mathcal{A}_3$", fontsize=ft) - -plt.savefig("Figure9.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() diff --git a/examples/IJOC2024+/README.rst b/examples/IJOC2024+/README.rst deleted file mode 100644 index e41f85a..0000000 --- a/examples/IJOC2024+/README.rst +++ /dev/null @@ -1,52 +0,0 @@ -Examples -~~~~~~~~ - -These examples replicate the results presented in the paper titled 'Performance Analysis of -Sequential Experimental Design for Calibration in Parallel Computing Environments' -by Sürer and Wild (2024). - -**Instructions for running the illustrative examples with performance model** - -To replicate Figures~3 and 7--10: - -1) Go to the ``examples/`` directory. - -2) Execute any of the following from the command line: - -.. code-block:: python - - python Fig3.py - python Fig7.py - python Fig8.py - python Fig9.py - python Fig10.py - -Running each script should not take more than 90 sec. See the figures (jpeg files) saved under ``examples/`` directory. - -**Instructions for running the sequential design using different synthetic simulation models** - -To replicate Figure~2, execute the following from the command line: - -.. code-block:: python - - python Fig2.py - -Running this script takes about a minute on a personal Mac laptop. - -To collect ``-max_eval`` simulation outputs from parameters acquired with -different acquisition functions (``-al_func``) and synthetic simulation models (``-funcname``), -one can use the script ``run_test_funcs.py``. - -As an example, execute any of the following from the command line: - -.. code-block:: python - - python run_test_funcs.py -funcname ackley -al_func ei -max_eval 200 - python run_test_funcs.py -funcname easom -al_func ei -max_eval 200 - python run_test_funcs.py -funcname himmelblau -al_func eivar -max_eval 200 - python run_test_funcs.py -funcname holder -al_func eivar -max_eval 200 - python run_test_funcs.py -funcname matyas -al_func hybrid_ei -max_eval 200 - python run_test_funcs.py -funcname sphere -al_func hybrid_ei -max_eval 200 - -Once completed, ``Figure_funcname.jpg`` is saved under ``examples/`` directory. -Running each script should not take more than one minute on a personal Mac laptop. \ No newline at end of file diff --git a/examples/IJOC2024+/Workshop_files/WS0.py b/examples/IJOC2024+/Workshop_files/WS0.py deleted file mode 100644 index 3d4dd6b..0000000 --- a/examples/IJOC2024+/Workshop_files/WS0.py +++ /dev/null @@ -1,113 +0,0 @@ -from plotutils import plotresult, plotparams -import matplotlib.pyplot as plt -import numpy as np - -# Generates Fig 3, 4, 11, 12 -clist = ["b", "r", "g", "m", "y", "c"] -mlist = ["P", "o", "*", "s", "p", "h"] -linelist = ["-", "--", "-.", ":", "-.", ":"] - -lw = 5 -ms = 15 -me = 200 - -labelsb = ["EIVAR"] - -batch = 1 -worker = 2 -rep = 0 -fonts = 25 - -path = "/Users/ozgesurer/Desktop/WS_data/batch_newjobs/" -ex = "himmelblau" -m = "eivar" -markers = ["o", "+", "*", "P", "^", "D", "s", "p", "h"] -colors = [ - "red", - "blue", - "orange", - "magenta", - "cyan", - "green", - "purple", - "yellow", - "pink", -] - -n = 2560 -ft = 20 -fig, axes = plt.subplots(1, 3, figsize=(20, 5)) -for id_b, b in enumerate([1, 2, 4, 8, 16, 32, 64, 128, 256]): - - out = "b" + str(b) - avgAE, avgtime, avgTV = plotresult(path, out, ex, b + 1, b, rep, m, n0=0, nf=n + 1) - - # if b == 256: - # print(avgTV[-1]) - - print(np.where(avgTV <= 1.7536062885144855e-05)[0][0]) - - axes[0].plot(np.arange(0, n + 1), avgTV, c=colors[id_b]) - - axes[0].plot( - np.arange(0, n + 1), - avgTV, - markers[id_b], - markevery=256, - markersize=ms, - label=str(b), - c=colors[id_b], - ) - - axes[1].plot( - np.arange(0, len(np.unique(avgTV))), - sorted(np.unique(avgTV), reverse=True), - c=colors[id_b], - ) - - axes[1].plot( - np.arange(0, len(np.unique(avgTV))), - sorted(np.unique(avgTV), reverse=True), - markers[id_b], - markersize=ms, - label=str(b), - c=colors[id_b], - ) - - print(sum(avgtime)) - - axes[2].scatter( - np.arange(0, len(avgtime[0:][avgtime[0:] > 0])), - avgtime[0:][avgtime[0:] > 0], - marker=markers[id_b], - s=ms * 2, - label=str(b), - c=colors[id_b], - ) - - -axes[0].set_xlabel("# of sim evals", fontsize=ft) -axes[0].set_ylabel("MAD", fontsize=ft) -axes[1].set_xlabel("# of stages", fontsize=ft) -axes[1].set_ylabel("MAD", fontsize=ft) -axes[2].set_xlabel("# of stages", fontsize=ft) -axes[2].set_ylabel("Acquisition time", fontsize=ft) - -axes[1].set_xscale("log") -axes[0].set_yscale("log") -axes[1].set_yscale("log") -axes[2].set_yscale("log") -axes[2].set_xscale("log") - -axes[2].tick_params(axis="both", which="major", labelsize=ft - 10) -axes[0].tick_params(axis="both", which="major", labelsize=ft - 10) -axes[1].tick_params(axis="x", which="major", labelsize=ft - 10) -axes[1].tick_params(axis="y", which="major", length=0) - - -axes[0].legend( - loc="upper center", bbox_to_anchor=(1.6, -0.25), ncol=9, fontsize=fonts - 10 -) -# fig.tight_layout() -plt.savefig("Figure_WS_0.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() diff --git a/examples/IJOC2024+/Workshop_files/WS0_run.py b/examples/IJOC2024+/Workshop_files/WS0_run.py deleted file mode 100644 index 045bf7d..0000000 --- a/examples/IJOC2024+/Workshop_files/WS0_run.py +++ /dev/null @@ -1,101 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments, save_output -import scipy.stats as sps -from PUQ.prior import prior_dist -from test_funcs import holder, ackley, easom, sphere, matyas, himmelblau -import matplotlib.pyplot as plt -import time - -start = time.time() - -args = parse_arguments() - -cls_data = eval(args.funcname)() - -# # # Create a mesh for test set # # # -xpl = np.linspace(cls_data.thetalimits[0][0], cls_data.thetalimits[0][1], 50) -ypl = np.linspace(cls_data.thetalimits[1][0], cls_data.thetalimits[1][1], 50) -Xpl, Ypl = np.meshgrid(xpl, ypl) -th = np.vstack([Xpl.ravel(), Ypl.ravel()]) -setattr(cls_data, "theta", th.T) - -ftest = np.zeros(2500) -for tid, t in enumerate(th.T): - ftest[tid] = cls_data.function(t[0], t[1]) -thetatest = th.T -ptest = np.zeros(thetatest.shape[0]) -for i in range(ftest.shape[0]): - mean = ftest[i] - rnd = sps.multivariate_normal(mean=mean, cov=cls_data.obsvar) - ptest[i] = rnd.pdf(cls_data.real_data) - -test_data = {"theta": thetatest, "f": ftest, "p": ptest, "p_prior": 1} - -# # # # # # # # # # # # # # # # # # # # # -prior_func = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] -) -# # # # # # # # # # # # # # # # # # # # # - -init_seeds = 0 # args.init_seeds -final_seeds = 1 # args.final_seeds -args.max_eval = 512 -n_init = 10 -s = 0 - -for b in [32]: - - args.nworkers = 257 - args.minibatch = b - - thetainit = prior_func.rnd(n_init, s) - finit = np.zeros(n_init) - for tid, t in enumerate(thetainit): - finit[tid] = cls_data.function(t[0], t[1]) - test_data["thetainit"] = thetainit - test_data["finit"] = finit[None, :] - - al_data = designer( - data_cls=cls_data, - method="SEQCALOPT", - args={ - "mini_batch": args.minibatch, - "nworkers": args.minibatch + 1, - "AL": "eivar", - "seed_n0": int(s), - "prior": prior_func, - "data_test": test_data, - "max_evals": args.max_eval, - "candsize": args.candsize, - "refsize": args.refsize, - "believer": 2, - }, - ) - save_output( - al_data, cls_data.data_name, args.al_func, args.nworkers, args.minibatch, int(s) - ) - - plt.plot(al_data._info["TV"]) - # theta_al = al_data._info["theta"] - # ft = 20 - # ms = 50 - # fig, ax = plt.subplots(1, 1, figsize=(8, 6)) - # cp = ax.contour(Xpl, Ypl, ptest.reshape(50, 50), 20, cmap="RdGy") - # ax.scatter(theta_al[:, 0], theta_al[:, 1], c="black", marker="+", s=ms, zorder=2) - # ax.scatter( - # thetainit[:, 0], - # thetainit[:, 1], - # zorder=2, - # marker="o", - # facecolors="none", - # edgecolors="blue", - # ) - # ax.set_xlabel(r"$\theta_1$", fontsize=ft) - # ax.set_ylabel(r"$\theta_2$", fontsize=ft) - # ax.tick_params(axis="both", labelsize=ft) - # plt.show() -plt.yscale("log") -plt.show() -end = time.time() -print("Elapsed time =", round(end - start, 3)) diff --git a/examples/IJOC2024+/Workshop_files/WS1_real.py b/examples/IJOC2024+/Workshop_files/WS1_real.py deleted file mode 100644 index 3b52907..0000000 --- a/examples/IJOC2024+/Workshop_files/WS1_real.py +++ /dev/null @@ -1,99 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from PUQ.performance import performanceModel -from matplotlib.ticker import FixedFormatter -from plotutils import plotresult - - -repno = 5 -n = 2560 -worker = 256 -batches = [1, 2, 4, 8, 16, 32, 64, 128, 256] -s_mean = [2, 2**6] -a_mean = [ - 2 ** (-2), - 2 ** (-1), - 2 ** (0), - 2 ** (1), - 2 ** (2), - 2 ** (3), - 2 ** (4), - 2 ** (5), -] -acq_tick = [ - r"$2^{-2}$", - r"$2^{-1}$", - r"$2^{0}$", - r"$2^1$", - r"$2^2$", - r"$2^3$", - r"$2^4$", - r"$2^5$", -] - -acclevel = 1.7536062885144855e-05 -rep = 0 -ex = "himmelblau" -m = "eivar" -path = "/Users/ozgesurer/Desktop/WS_data/batch_newjobs/" - -fig, ax = plt.subplots(1, 2, figsize=(12, 5)) -for sid, sm in enumerate(s_mean): - result = [] - for aid, am in enumerate(a_mean): - for r in range(repno): - for id_b, b in enumerate(batches): - PM = performanceModel(worker=worker, batch=b, n=n, n0=worker) - PM.gen_acqtime(am, am, 0, typeGen="linear") - PM.gen_simtime(sm, sm, 0.01, typeSim="normal", seed=r) - - out = "b" + str(b) - avgAE, avgtime, avgTV = plotresult( - path, out, ex, b + 1, b, rep, m, n0=0, nf=n + 1 - ) - PM.acc = avgTV - - PM.simulate() - PM.summarize() - PM.complete(acclevel) - - result.append( - { - "r": r, - "b": b, - "am": am, - "var": 1, - "sm": sm, - "res": PM, - } - ) - - timemat = np.zeros((len(a_mean), len(batches))) - bmat = np.zeros((len(a_mean), len(batches))) - for aid, am in enumerate(a_mean): - for bid, b in enumerate(batches): - res_c = [res for res in result if ((res["am"] == am) & (res["b"] == b))] - timemat[aid, bid] = np.mean( - [r["res"].complete_time for r in res_c] - ) # res_c[0]["res"].complete_time - - ft = 20 - bo = np.argsort(np.argsort(timemat, axis=1), axis=1) - im = ax[sid].imshow(bo, aspect="auto", cmap="YlOrRd") - cbar = fig.colorbar( - im, - ticks=np.array([0.0, 0.5, 1.0]) * bo.max(), - format=FixedFormatter(["low", "mid", "high"]), - ) - cbar.ax.tick_params(labelsize=ft - 5) - cbar.ax.set_ylabel("Wall-Clock Time", rotation=-90, va="bottom", fontsize=ft - 5) - # # Show all ticks and label them with the respective list entries - ax[sid].set_xticks(np.arange(len(batches)), labels=batches) - ax[sid].set_yticks(np.arange(len(a_mean)), labels=acq_tick) - ax[sid].set_xlabel("Batch Size", fontsize=ft) - ax[sid].set_ylabel("Acquisition Time", fontsize=ft) - ax[sid].tick_params(axis="both", which="major", labelsize=ft - 5) -fig.suptitle("Simulation Time Increases \u2192", fontsize=ft) -fig.tight_layout() -plt.savefig("Figure_WS_1.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() diff --git a/examples/IJOC2024+/Workshop_files/WS2_real.py b/examples/IJOC2024+/Workshop_files/WS2_real.py deleted file mode 100644 index 7705805..0000000 --- a/examples/IJOC2024+/Workshop_files/WS2_real.py +++ /dev/null @@ -1,135 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from PUQ.performance import performanceModel -from mpl_toolkits.axes_grid1 import make_axes_locatable -from plotutils import plotresult - -repno = 5 -n = 2560 -varlist = [0.01, 1] -worker = 256 -batches = [1, 2, 4, 8, 16, 32, 64, 128, 256] -s_mean = [2**0, 2**1, 2**2, 2**3, 2**4, 2**5] -a_mean = [2 ** (-5), 2 ** (-3), 2 ** (-1), 2, 2**3, 2**5] -sim_tick = [r"$2^0$", r"$2^1$", r"$2^2$", r"$2^3$", r"$2^4$", r"$2^5$"] -acq_tick = [r"$2^{-5}$", r"$2^{-3}$", r"$2^{-1}$", r"$2^{1}$", r"$2^3$", r"$2^5$"] -acclevel = 1.7536062885144855e-05 -rep = 0 -ex = "himmelblau" -m = "eivar" -path = "/Users/ozgesurer/Desktop/WS_data/batch_newjobs/" - -fig, ax = plt.subplots(1, 2, figsize=(12, 6)) -for vid, var in enumerate(varlist): - result = [] - for aid, am in enumerate(a_mean): - for sid, sm in enumerate(s_mean): - res = [] - for r in range(repno): - for id_b, b in enumerate(batches): - PM = performanceModel(worker=worker, batch=b, n=n, n0=worker) - PM.gen_acqtime(am, am, 0, typeGen="linear") - PM.gen_simtime(sm, sm * var, 0.01, typeSim="normal", seed=r) - - out = "b" + str(b) - avgAE, avgtime, avgTV = plotresult( - path, out, ex, b + 1, b, rep, m, n0=0, nf=n + 1 - ) - PM.acc = avgTV - - PM.simulate() - PM.summarize() - PM.complete(acclevel) - - result.append( - { - "r": r, - "b": b, - "am": am, - "var": var, - "sm": sm, - "res": PM, - } - ) - - timemat = np.zeros((len(a_mean), len(s_mean))) - bmat = np.zeros((len(a_mean), len(s_mean))) - for aid, am in enumerate(a_mean): - for sid, sm in enumerate(s_mean): - res_c = [res for res in result if ((res["am"] == am) & (res["sm"] == sm))] - - rmin = np.inf - bmin = np.inf - for bid, b in enumerate(batches): - cs = [rs["res"].complete_time for rs in res_c if rs["b"] == b] - if np.mean(cs) < rmin: - rmin = np.mean(cs) - bmin = b - - timemat[aid, sid] = rmin - bmat[aid, sid] = bmin - import matplotlib.colors as mcolors - - bounds = [ - 0.5, - 1.5, - 3.5, - 7.5, - 15.5, - 31.5, - 63.5, - 127.5, - 255.5, - 512, - ] # bounds to differentiate colors - cmap = plt.get_cmap("YlOrRd", len(batches)) - norm = mcolors.BoundaryNorm(bounds, cmap.N) # Normalize colors based on bounds - - # Heatmap - ft = 20 - divider = make_axes_locatable(ax[vid]) - cax = divider.append_axes("right", size="5%", pad=0.05) - im = ax[vid].imshow(bmat, cmap=cmap, norm=norm) - - # Show all ticks and label them with the respective list entries - ax[vid].set_xticks(np.arange(len(s_mean)), labels=sim_tick) - ax[vid].set_yticks(np.arange(len(a_mean)), labels=acq_tick) - ax[vid].tick_params(axis="both", which="major", labelsize=ft - 5) - # Rotate the tick labels and set their alignment. - plt.setp(ax[vid].get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor") - - # Loop over data dimensions and create text annotations. - for i in range(len(a_mean)): - for j in range(len(s_mean)): - if bmat[i, j] >= 64: - text = ax[vid].text( - j, - i, - int(timemat[i, j]), - ha="center", - va="center", - color="white", - fontsize=ft - 5, - ) - else: - text = ax[vid].text( - j, - i, - int(timemat[i, j]), - ha="center", - va="center", - color="black", - fontsize=ft - 5, - ) - - ax[vid].grid(which="minor", color="w", linestyle="-", linewidth=3) - ax[vid].set_xlabel("Simulation Time", fontsize=ft) - ax[vid].set_ylabel("Acquisition Time", fontsize=ft) - cbar = fig.colorbar(im, cax=cax, orientation="vertical") - cbar.ax.tick_params(labelsize=ft - 5) - cbar.ax.set_ylabel("Batch Size", rotation=-90, va="bottom", fontsize=ft - 5) - cbar.set_ticks(batches) -fig.suptitle("Variability in Simulation Time Increases \u2192", fontsize=ft) -fig.tight_layout() -plt.savefig("Figure_WS_2.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() diff --git a/examples/IJOC2024+/ci_example.py b/examples/IJOC2024+/ci_example.py deleted file mode 100644 index 2e7a95c..0000000 --- a/examples/IJOC2024+/ci_example.py +++ /dev/null @@ -1,66 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments -import scipy.stats as sps -from PUQ.prior import prior_dist -from test_funcs import holder, ackley, easom, sphere, matyas, himmelblau - -if __name__ == "__main__": - args = parse_arguments() - - cls_data = eval(args.funcname)() - - # # # Create a mesh for test set # # # - xpl = np.linspace(cls_data.thetalimits[0][0], cls_data.thetalimits[0][1], 50) - ypl = np.linspace(cls_data.thetalimits[1][0], cls_data.thetalimits[1][1], 50) - Xpl, Ypl = np.meshgrid(xpl, ypl) - th = np.vstack([Xpl.ravel(), Ypl.ravel()]) - setattr(cls_data, "theta", th.T) - - ftest = np.zeros(2500) - for tid, t in enumerate(th.T): - ftest[tid] = cls_data.function(t[0], t[1]) - thetatest = th.T - ptest = np.zeros(thetatest.shape[0]) - for i in range(ftest.shape[0]): - mean = ftest[i] - rnd = sps.multivariate_normal(mean=mean, cov=cls_data.obsvar) - ptest[i] = rnd.pdf(cls_data.real_data) - - test_data = {"theta": thetatest, "f": ftest, "p": ptest, "p_prior": 1} - - # # # # # # # # # # # # # # # # # # # # # - prior_func = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - # # # # # # # # # # # # # # # # # # # # # - - init_seeds = args.init_seeds - final_seeds = args.final_seeds - - n_init = 10 - for s in np.arange(init_seeds, final_seeds): - - thetainit = prior_func.rnd(n_init, s) - finit = np.zeros(n_init) - for tid, t in enumerate(thetainit): - finit[tid] = cls_data.function(t[0], t[1]) - test_data["thetainit"] = thetainit - test_data["finit"] = finit[None, :] - - al_data = designer( - data_cls=cls_data, - method="SEQCALOPT", - args={ - "mini_batch": 1, - "nworkers": 2, - "AL": args.al_func, - "seed_n0": int(s), - "prior": prior_func, - "data_test": test_data, - "max_evals": args.max_eval, - "candsize": args.candsize, - "refsize": args.refsize, - "believer": args.believer, - }, - ) diff --git a/examples/IJOC2024+/compile_results.py b/examples/IJOC2024+/compile_results.py deleted file mode 100644 index 24dd9e7..0000000 --- a/examples/IJOC2024+/compile_results.py +++ /dev/null @@ -1,183 +0,0 @@ -from plotutils import plotresult, plotparams -import matplotlib.pyplot as plt -import numpy as np - -clist = ["b", "r", "g", "m", "y", "c", "pink", "purple"] -mlist = ["P", "p", "*", "o", "s", "h"] -linelist = ["-", "--", "-.", ":", "-.", ":"] - -labelsb = ["EI", "EIVAR", "HYBRID", "RND"] -method = ["ei", "eivar", "hybrid_ei", "rnd"] -example_name = ["sphere", "matyas", "ackley"] -example_name = ["himmelblau", "holder", "easom"] - -batch = 1 -worker = 2 -rep = 30 -fonts = 22 - -path = "/Users/ozgesurer/Desktop/sh_files/" - -for metric in ["AE", "MAD"]: - fig, axes = plt.subplots(1, 3, figsize=(22, 5)) - for exid, ex in enumerate(example_name): - for mid, m in enumerate(method): - out = ex + "_" + m - avgAE, avgtime, avgTV = plotresult( - path, out, ex, worker, batch, rep, m, n0=10, nf=1000 - ) - if metric == "AE": - axes[exid].plot( - np.arange(len(avgAE)), - avgAE, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - ) - else: - axes[exid].plot( - np.arange(len(avgTV)), - avgTV, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - ) - axes[exid].set_yscale("log") - # axes[exid].set_xscale('log') - axes[exid].set_xlabel("# of parameters", fontsize=fonts) - if exid == 0: - if metric == "AE": - axes[exid].set_ylabel(r"$\delta$", fontsize=fonts) - else: - axes[exid].set_ylabel(r"MAD", fontsize=fonts) - axes[exid].tick_params(axis="both", which="major", labelsize=fonts - 5) - - axes[1].legend(bbox_to_anchor=(1.3, -0.2), ncol=4, fontsize=fonts) - plt.show() - -show1 = False -if show1: - labelsb = ["10", "100", "1000", "RND"] - method = ["hybrid_ei", "hybrid_ei", "hybrid_ei", "rnd"] - outs = [10, 100, 1000] - example_name = ["himmelblau"] - - for metric in ["AE"]: - fig, axes = plt.subplots(1, 1, figsize=(5, 5)) - for exid, ex in enumerate(example_name): - for mid, m in enumerate(method): - if mid < 3: - out = ex + "_" + m + "_" + str(outs[mid]) - else: - out = ex + "_" + m - avgAE, avgtime, avgTV = plotresult( - path, out, ex, worker, batch, rep, m, n0=10, nf=1000 - ) - if metric == "AE": - axes.plot( - np.arange(len(avgAE)), - avgAE, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - ) - else: - axes.plot( - np.arange(len(avgTV)), - avgTV, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - ) - axes.set_yscale("log") - axes.set_xscale("log") - axes.set_xlabel("# of parameters", fontsize=fonts) - if exid == 0: - if metric == "AE": - axes.set_ylabel(r"$\delta$", fontsize=fonts) - else: - axes.set_ylabel(r"MAD", fontsize=fonts) - axes.tick_params(axis="both", which="major", labelsize=fonts - 5) - - axes.legend(bbox_to_anchor=(1.3, -0.2), ncol=4, fontsize=fonts) - plt.show() - - -show2 = True -if show2: - - clist = ["b", "r", "g", "m", "y", "c", "pink", "purple"] - mlist = ["P", "p", "*", "o", "s", "h"] - linelist = ["-", "--", "-.", ":", "-.", ":"] - - labelsb = ["b=1", "b=32", "b=64", "b=128"] - method = ["ei"] - batch_sizes = [1, 32, 64, 128] - batch_sizes = [1, 5, 25, 125] - example_name = ["sphere", "matyas", "ackley"] - # example_name = ['matyas'] - # example_name = ['himmelblau', 'holder', 'easom'] - worker = 126 - rep = 28 - fonts = 22 - - path = "/Users/ozgesurer/Desktop/sh_files/batch_mode/" - - for metric in ["AE", "MAD"]: - fig, axes = plt.subplots(1, 3, figsize=(22, 5)) - for exid, ex in enumerate(example_name): - for bid, b in enumerate(batch_sizes): - for mid, m in enumerate(method): - out = ex + "_" + m + "_" + "b" + str(b) + "_" + "w128" - avgAE, avgtime, avgTV = plotresult( - path, out, ex, worker, b, rep, m, n0=128, nf=1000 - ) - if metric == "AE": - axes[exid].plot( - np.arange(len(avgAE)), - avgAE, - label=labelsb[bid], - color=clist[bid], - linestyle=linelist[bid], - ) - else: - axes[exid].plot( - np.arange(len(avgTV)), - avgTV, - label=labelsb[bid], - color=clist[bid], - linestyle=linelist[bid], - ) - axes[exid].set_yscale("log") - # axes[exid].set_xscale('log') - axes[exid].set_xlabel("# of parameters", fontsize=fonts) - if exid == 0: - if metric == "AE": - axes[exid].set_ylabel(r"$\delta$", fontsize=fonts) - else: - axes[exid].set_ylabel(r"MAD", fontsize=fonts) - axes[exid].tick_params(axis="both", which="major", labelsize=fonts - 5) - - axes[1].legend(bbox_to_anchor=(1.3, -0.2), ncol=4, fontsize=fonts) - plt.show() - - -show3 = False -if show3: - - clist = ["b", "r", "g", "m", "y", "c", "pink", "purple"] - mlist = ["P", "p", "*", "o", "s", "h"] - linelist = ["-", "--", "-.", ":", "-.", ":"] - - labelsb = ["b=1", "b=32", "b=64", "b=128"] - m = "ei" - - ex = "easom" - worker = 129 - rep = 30 - fonts = 22 - - path = "/Users/ozgesurer/Desktop/sh_files/batch_mode/" - b = 32 - out = ex + "_" + m + "_" + "b" + str(b) + "_" + "w128" - plotparams(path, out, ex, worker, b, rep, m, n0=128, nf=1000) diff --git a/examples/IJOC2024+/plotutils.py b/examples/IJOC2024+/plotutils.py deleted file mode 100644 index df209cf..0000000 --- a/examples/IJOC2024+/plotutils.py +++ /dev/null @@ -1,51 +0,0 @@ -from PUQ.designmethods.utils import parse_arguments, save_output, read_output -import matplotlib.pyplot as plt -import matplotlib -import numpy as np - - -def plotresult(path, out, ex_name, w, b, rep, method, n0, nf): - - AElist = [] - TVlist = [] - timelist = [] - for i in range(1, 1 + rep): - design_saved = read_output(path + out + "/", ex_name, method, w, b, i) - - TV = design_saved._info["TV"] - AE = design_saved._info["AE"] - time = design_saved._info["time"] - - if method == "rnd": - time = np.repeat(0.1, len(time)) - - bestTV = np.zeros(TV.shape) - for i in range(len(TV)): - bestTV[i] = np.min(TV[0 : (i + 1)]) - - AElist.append(AE[n0:nf]) - timelist.append(time[n0:nf]) - TVlist.append(bestTV[n0:nf]) - - avgtime = np.mean(np.array(timelist), 0) - avgAE = np.mean(np.array(AElist), 0) - avgTV = np.mean(np.array(TVlist), 0) - - return avgAE, avgtime, avgTV - - -def plotparams(path, out, ex_name, w, b, rep, method, n0, nf, thetalim): - - lst = [] - for i in range(1, 1 + rep): - design_saved = read_output(path + out + "/", ex_name, method, w, b, i) - lst.append(design_saved._info["AE"][nf]) - # print(design_saved._info['AE'][-1]) - theta = design_saved._info["theta"] - plt.scatter(theta[:, 0], theta[:, 1]) - plt.xlim(thetalim[0][0], thetalim[0][1]) - plt.ylim(thetalim[1][0], thetalim[1][1]) - plt.show() - - print(len(design_saved._info["AE"])) - print(np.mean(lst)) diff --git a/examples/IJOC2024+/plotv0/Fig10.py b/examples/IJOC2024+/plotv0/Fig10.py deleted file mode 100644 index 368f7a6..0000000 --- a/examples/IJOC2024+/plotv0/Fig10.py +++ /dev/null @@ -1,182 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from PUQ.performance import performanceModel -from PUQ.performanceutils.utils import ( - plot_acqtime, - plot_endtime, - plot_errorend, -) -import time - -start = time.time() - -repno = 2 -varlist = [1] -acclevel = 0.2 -n = 2048 -workers = [16, 32, 64, 128, 256, 512] -batches = [4, 8, 16] -simmeans = [2, 4, 16] -accparams = [[-1, 0.2], [-1, 0.21], [-1, 0.22]] -genparams = [[0.2, 0.2]] - -result = [] -ft = 25 -lw = 5 -ms = 15 -me = 200 -for sid, sim_mean in enumerate(simmeans): - for varid, var in enumerate(varlist): - for id_b, b in enumerate(batches): - res = [] - for r in range(repno): - for id_w, w in enumerate(workers): - PM = performanceModel(worker=w, batch=b, n=n, n0=w) - PM.gen_acqtime(genparams[0][0], 0.001, typeGen="constant") - PM.gen_simtime( - sim_mean, sim_mean * var, 0.001, typeSim="normal", seed=r - ) - PM.gen_curve( - accparams[id_b][0], accparams[id_b][1], typeAcc="exponential" - ) - PM.simulate() - PM.summarize() - PM.complete(acclevel) - - result.append( - { - "r": r, - "b": b, - "var": var, - "w": w, - "simmean": sim_mean, - "res": PM, - } - ) - -clist = ["b", "r", "g", "m", "y", "c"] -mlist = ["P", "o", "*", "s", "p", "h"] -linelist = ["-", "--", "-.", ":", "-.", ":"] - -labs = ["b=4", "b=8", "b=16"] -for varid, var in enumerate(varlist): - fig, axes = plt.subplots(3, len(simmeans), figsize=(24, 18)) - for sid, sim_mean in enumerate(simmeans): - - res_c = [ - res - for res in result - if ((res["simmean"] == sim_mean) & (res["var"] == var)) - ] - for bid, b in enumerate(batches): - endtime = [] - idletime = [] - computetime = [] - for wid, w in enumerate(workers): - endtime.append( - np.mean( - [ - res["res"].complete_time - for res in res_c - if ((res["w"] == w) & (res["b"] == b)) - ] - ) - ) - idletime.append( - np.mean( - [ - res["res"].avg_idle_time - for res in res_c - if ((res["w"] == w) & (res["b"] == b)) - ] - ) - ) - computetime.append( - np.mean( - [ - res["res"].computing_hours - for res in res_c - if ((res["w"] == w) & (res["b"] == b)) - ] - ) - ) - - endtimescaled = [e / (endtime[0]) for e in endtime] - axes[0, sid].plot( - workers, - endtimescaled, - marker=mlist[bid], - markersize=ms, - linestyle=linelist[bid], - linewidth=lw, - label=labs[bid], - color=clist[bid], - ) - axes[1, sid].plot( - workers, - idletime, - marker=mlist[bid], - markersize=ms, - linestyle=linelist[bid], - linewidth=lw, - label=labs[bid], - color=clist[bid], - ) - axes[2, sid].plot( - workers, - computetime, - marker=mlist[bid], - markersize=ms, - linestyle=linelist[bid], - linewidth=lw, - label=labs[bid], - color=clist[bid], - ) - - axes[0, sid].set_xscale("log") - axes[0, sid].set_yscale("log") - axes[0, sid].set_xticks(workers) - axes[0, sid].set_xticklabels(workers) - axes[0, sid].tick_params(axis="both", which="major", labelsize=ft - 5) - - axes[1, sid].set_xscale("log") - axes[1, sid].set_yscale("log") - axes[1, sid].set_xticks(workers) - axes[1, sid].set_xticklabels(workers) - axes[1, sid].tick_params(axis="both", which="major", labelsize=ft - 5) - - axes[2, sid].set_xscale("log") - axes[2, sid].set_yscale("log") - axes[2, sid].set_xticks(workers) - axes[2, sid].set_xticklabels(workers) - axes[2, sid].tick_params(axis="both", which="major", labelsize=ft - 5) - axes[2, sid].set_xlabel("# of workers", fontsize=ft) - - axes[0, sid].plot( - workers, - [1, 1 / 2, 1 / 4, 1 / 8, 1 / 16, 1 / 32], - color="black", - linestyle=linelist[4], - linewidth=4, - ) - axes[0, 0].set_ylabel("Wall-clock time (scaled)", fontsize=ft) - axes[1, 0].set_ylabel("Idle time", fontsize=ft) - axes[2, 0].set_ylabel("Computing hours", fontsize=ft) - if varid == len(varlist) - 1: - handles, labels = axes[0, 0].get_legend_handles_labels() - fig.legend( - handles, - labels, - loc="upper center", - title_fontsize=ft, - bbox_to_anchor=(0.5, 0.06), - ncol=4, - prop={"size": ft}, - fancybox=True, - shadow=True, - ) - plt.savefig("Figure9.jpg", format="jpeg", bbox_inches="tight", dpi=500) - plt.show() - -end = time.time() -print("Elapsed time =", round(end - start, 3)) diff --git a/examples/IJOC2024+/plotv0/Fig8.py b/examples/IJOC2024+/plotv0/Fig8.py deleted file mode 100644 index 95ff46e..0000000 --- a/examples/IJOC2024+/plotv0/Fig8.py +++ /dev/null @@ -1,131 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from PUQ.performance import performanceModel -from PUQ.performanceutils.utils import ( - plot_acqtime, - plot_endtime, - plot_errorend, -) -import time - -start = time.time() - -repno = 1 -n = 2560 -varlist = [0.1, 10] -worker = 256 -batches = [1, 2, 4, 8, 16, 32, 64, 128, 256] -simmeans = [0.1, 1, 10] -acqscale = [0.01, 0.1, 1] -accparams = [ - [-1, 0.2], - [-1, 0.22], - [-1, 0.24], - [-1, 0.26], - [-1, 0.28], - [-1, 0.3], - [-1, 0.32], - [-1, 0.34], - [-1, 0.36], -] -acclevel = 0.2 -clist = ["b", "r", "g", "m", "y", "c"] -mlist = ["P", "o", "*", "s", "p", "h"] -linelist = ["-", "--", "-.", ":", "-.", ":"] -thrlist = [] - -lab = ["b=1", "b=2", "b=4", "b=8", "b=16", "b=32", "b=64", "b=128", "b=256"] -lw = 5 -ms = 15 -me = 200 - -result = [] -for scaleid, scale in enumerate(acqscale): - for varid, var in enumerate(varlist): - for sid, sim_mean in enumerate(simmeans): - res = [] - for r in range(repno): - for id_b, b in enumerate(batches): - PM = performanceModel(worker=worker, batch=b, n=n, n0=worker) - PM.gen_acqtime(scale, scale, 0.001, typeGen="linear") - PM.gen_simtime( - sim_mean, sim_mean * var, 0.01, typeSim="normal", seed=r - ) - PM.gen_curve(-1, accparams[id_b][1], typeAcc="exponential") - PM.simulate() - PM.summarize() - PM.complete(acclevel) - - result.append( - { - "r": r, - "b": b, - "scale": scale, - "var": var, - "simmean": sim_mean, - "res": PM, - } - ) - -ft = 25 -fig, axes = plt.subplots(2, len(acqscale), figsize=(24, 12)) -for varid, var in enumerate(varlist): - - for scaleid, scale in enumerate(acqscale): - - res_c = [ - res for res in result if ((res["scale"] == scale) & (res["var"] == var)) - ] - for sid, s in enumerate(simmeans): - endtime = [] - - for bid, b in enumerate(batches): - endtime.append( - np.mean( - [ - res["res"].complete_time - for res in res_c - if ((res["b"] == b) & (res["simmean"] == s)) - ] - ) - ) - - axes[varid, scaleid].plot( - batches, - endtime, - marker=mlist[sid], - markersize=ms, - linestyle=linelist[sid], - linewidth=lw, - label=str(s), - color=clist[sid], - ) - axes[varid, scaleid].set_xscale("log") - axes[varid, scaleid].set_yscale("log") - axes[varid, scaleid].set_xticks(batches) - axes[varid, scaleid].set_xticklabels(batches) - axes[varid, scaleid].tick_params( - axis="both", which="major", labelsize=ft - 5 - ) - axes[varid, scaleid].set_xlabel("b", fontsize=ft) - axes[varid, 0].set_ylabel("Wall-clock time", fontsize=ft) - if varid == len(varlist) - 1: - handles, labels = axes[varid, 0].get_legend_handles_labels() - - labels = [r"$\tilde{a}$=" + l for l in labels] - fig.legend( - handles, - labels, - loc="upper center", - title_fontsize=ft, - bbox_to_anchor=(0.5, 0.06), - ncol=4, - prop={"size": ft}, - fancybox=True, - shadow=True, - ) -plt.savefig("Figure7.jpg", format="jpeg", bbox_inches="tight", dpi=500) -plt.show() - -end = time.time() -print("Elapsed time =", round(end - start, 3)) diff --git a/examples/IJOC2024+/plotv0/Fig9.py b/examples/IJOC2024+/plotv0/Fig9.py deleted file mode 100644 index d1f7f5f..0000000 --- a/examples/IJOC2024+/plotv0/Fig9.py +++ /dev/null @@ -1,122 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from PUQ.performance import performanceModel -from PUQ.performanceutils.utils import ( - plot_acqtime, - plot_endtime, - plot_errorend, -) -import time - -start = time.time() - -### ### ### ### ### ### - -repno = 10 -varlist = [1] -acclevel = 0.2 -n = 2048 -worker = 256 -n0 = worker -batches = [1, 2, 4, 8, 16, 32, 64, 128, 256] -simmeans = [4, 16, 64] -clist = ["b", "r", "g", "m", "y", "c"] -mlist = ["P", "o", "*", "s", "p", "h"] -linelist = ["-", "--", "-.", ":", "-.", ":"] -lab = ["b=1", "b=2", "b=4", "b=8", "b=16", "b=32", "b=64", "b=128", "b=256"] -accparams = [[-1, 0.2], [-1, 0.3], [-1, 0.4]] -genparams = [1, 0.5, 0.25] -result = [] -ft = 25 -lw = 5 -ms = 15 -me = 200 - -for sid, sim_mean in enumerate(simmeans): - for varid, var in enumerate(varlist): - for aid, acc in enumerate(accparams): - res = [] - for r in range(repno): - - for id_b, b in enumerate(batches): - PM = performanceModel(worker=worker, batch=b, n=n, n0=n0) - PM.gen_acqtime(genparams[aid], 0.001, typeGen="constant") - PM.gen_simtime( - simmeans[sid], simmeans[sid], 0.001, typeSim="normal", seed=r - ) - PM.gen_curve(acc[0], acc[1] + id_b * 0.01, typeAcc="exponential") - - PM.simulate() - PM.summarize() - PM.complete(acclevel) - - result.append( - { - "r": r, - "b": b, - "var": var, - "acc": aid, - "simmean": simmeans[sid], - "res": PM, - } - ) - -labs = [r"$\mathcal{A}_1$", r"$\mathcal{A}_2$", r"$\mathcal{A}_3$"] -for varid, var in enumerate(varlist): - fig, axes = plt.subplots(1, len(simmeans), figsize=(24, 6)) - for sid, sim_mean in enumerate(simmeans): - - res_c = [ - res - for res in result - if ((res["simmean"] == sim_mean) & (res["var"] == var)) - ] - for aid, acc in enumerate(accparams): - endtime = [] - - for bid, b in enumerate(batches): - endtime.append( - np.mean( - [ - res["res"].complete_time - for res in res_c - if ((res["b"] == b) & (res["acc"] == aid)) - ] - ) - ) - - axes[sid].plot( - batches, - endtime, - marker=mlist[aid], - markersize=ms, - linestyle=linelist[aid], - linewidth=lw, - label=labs[aid], - color=clist[aid], - ) - axes[sid].set_xscale("log") - axes[sid].set_yscale("log") - axes[sid].set_xticks(batches) - axes[sid].set_xticklabels(batches) - axes[sid].tick_params(axis="both", which="major", labelsize=ft - 5) - axes[sid].set_xlabel("b", fontsize=ft) - axes[0].set_ylabel("Wall-clock time", fontsize=ft) - if varid == len(varlist) - 1: - handles, labels = axes[0].get_legend_handles_labels() - fig.legend( - handles, - labels, - loc="upper center", - title_fontsize=ft, - bbox_to_anchor=(0.5, 0.01), - ncol=4, - prop={"size": ft}, - fancybox=True, - shadow=True, - ) - plt.savefig("Figure8.jpg", format="jpeg", bbox_inches="tight", dpi=500) - plt.show() - -end = time.time() -print("Elapsed time =", round(end - start, 3)) diff --git a/examples/IJOC2024+/result_read.py b/examples/IJOC2024+/result_read.py deleted file mode 100644 index 63734af..0000000 --- a/examples/IJOC2024+/result_read.py +++ /dev/null @@ -1,50 +0,0 @@ -import numpy as np -import dill as pickle -from PUQ.designmethods.utils import read_output - - -def get_rep_data(s, w, b, rep, filename, method): - - avgtime = 0 - avgAE = 0 - avgTV = 0 - avgbestTV = 0 - - AElist = [] - TVlist = [] - timelist = [] - for i in range(1, 1 + rep): - design_saved = read_output(filename, s, method, w, b, i) - - theta_al = design_saved._info["theta"] - TV = design_saved._info["TV"] - HD = design_saved._info["HD"] - AE = design_saved._info["AE"] - time = design_saved._info["time"] - - if method == "rnd": - time = np.repeat(0.1, len(time)) - - bestTV = np.zeros(TV.shape) - for i in range(len(TV)): - bestTV[i] = np.min(TV[0 : (i + 1)]) - - AElist.append(AE) - timelist.append(time) - TVlist.append(bestTV) - - avgtime += time - avgAE += AE - avgTV += TV - avgbestTV += bestTV - - avgtime = np.mean(np.array(timelist), 0) - sdtime = np.std(np.array(timelist), 0) / np.sqrt(30) - - avgAE = np.mean(np.array(AElist), 0) - sdAE = np.std(np.array(AElist), 0) / np.sqrt(30) - - avgTV = np.mean(np.array(TVlist), 0) - sdTV = np.std(np.array(TVlist), 0) / np.sqrt(30) - - return avgAE[10:], avgtime[20:] diff --git a/examples/IJOC2024+/run_test_funcs.py b/examples/IJOC2024+/run_test_funcs.py deleted file mode 100644 index 9330929..0000000 --- a/examples/IJOC2024+/run_test_funcs.py +++ /dev/null @@ -1,101 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments -import scipy.stats as sps -from PUQ.prior import prior_dist -from test_funcs import holder, ackley, easom, sphere, matyas, himmelblau -import matplotlib.pyplot as plt -import time - -if __name__ == "__main__": - start = time.time() - - args = parse_arguments() - - cls_data = eval(args.funcname)() - - # # # Create a mesh for test set # # # - xpl = np.linspace(cls_data.thetalimits[0][0], cls_data.thetalimits[0][1], 50) - ypl = np.linspace(cls_data.thetalimits[1][0], cls_data.thetalimits[1][1], 50) - Xpl, Ypl = np.meshgrid(xpl, ypl) - th = np.vstack([Xpl.ravel(), Ypl.ravel()]) - setattr(cls_data, "theta", th.T) - - ftest = np.zeros(2500) - for tid, t in enumerate(th.T): - ftest[tid] = cls_data.function(t[0], t[1]) - thetatest = th.T - ptest = np.zeros(thetatest.shape[0]) - for i in range(ftest.shape[0]): - mean = ftest[i] - rnd = sps.multivariate_normal(mean=mean, cov=cls_data.obsvar) - ptest[i] = rnd.pdf(cls_data.real_data) - - test_data = {"theta": thetatest, "f": ftest, "p": ptest, "p_prior": 1} - - # # # # # # # # # # # # # # # # # # # # # - prior_func = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - # # # # # # # # # # # # # # # # # # # # # - - init_seeds = args.init_seeds - final_seeds = args.final_seeds - - n_init = 10 - for s in np.arange(init_seeds, final_seeds): - - thetainit = prior_func.rnd(n_init, s) - finit = np.zeros(n_init) - for tid, t in enumerate(thetainit): - finit[tid] = cls_data.function(t[0], t[1]) - test_data["thetainit"] = thetainit - test_data["finit"] = finit[None, :] - - al_data = designer( - data_cls=cls_data, - method="SEQCALOPT", - args={ - "mini_batch": 1, - "nworkers": 2, - "AL": args.al_func, - "seed_n0": int(s), - "prior": prior_func, - "data_test": test_data, - "max_evals": args.max_eval, - "candsize": args.candsize, - "refsize": args.refsize, - "believer": args.believer, - }, - ) - - theta_al = al_data._info["theta"] - ft = 20 - ms = 50 - fig, ax = plt.subplots(1, 1, figsize=(8, 6)) - cp = ax.contour(Xpl, Ypl, ptest.reshape(50, 50), 20, cmap="RdGy") - ax.scatter( - theta_al[:, 0], theta_al[:, 1], c="black", marker="+", s=ms, zorder=2 - ) - ax.scatter( - thetainit[:, 0], - thetainit[:, 1], - zorder=2, - marker="o", - facecolors="none", - edgecolors="blue", - ) - ax.set_xlabel(r"$\theta_1$", fontsize=ft) - ax.set_ylabel(r"$\theta_2$", fontsize=ft) - ax.tick_params(axis="both", labelsize=ft) - - plt.savefig( - "Figure_" + args.funcname + ".jpg", - format="jpeg", - bbox_inches="tight", - dpi=500, - ) - plt.show() - - end = time.time() - print("Elapsed time =", round(end - start, 3)) diff --git a/examples/IJOC2024+/test_funcs.py b/examples/IJOC2024+/test_funcs.py deleted file mode 100644 index e23f30a..0000000 --- a/examples/IJOC2024+/test_funcs.py +++ /dev/null @@ -1,361 +0,0 @@ -import numpy as np -from threading import Event - - -def artificial_time(persis_info, sim_specs): - rand_stream = persis_info["rand_stream"] - run_time = rand_stream.normal(0.1, 0.1, 1) - if run_time[0] < 0.01: - r = 0.01 - else: - r = run_time[0] - Event().wait(r) - - -class banana: - def __init__(self): - self.data_name = "banana" - self.thetalimits = np.array([[-20, 20], [-10, 5]]) - self.obsvar = np.array([[10**2, 0], [0, 1]]) - self.real_data = np.array([[1, 3]], dtype="float64") - self.out = [("f", float, (2,))] - self.p = 2 - self.d = 2 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - f = np.array([theta1, theta2 + 0.03 * theta1**2]) - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the banana function - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class bimodal: - def __init__(self): - - self.data_name = "bimodal" - self.thetalimits = np.array([[-6, 6], [-4, 8]]) - self.obsvar = np.array([[1 / np.sqrt(0.2), 0], [0, 1 / np.sqrt(0.75)]]) - self.real_data = np.array([[0, 2]], dtype="float64") - self.out = [("f", float, (2,))] - self.d = 2 - self.p = 2 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - f = np.array([theta2 - theta1**2, theta2 - theta1]) - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the bimodal function - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class unimodal: - def __init__(self): - self.data_name = "unimodal" - self.thetalimits = np.array([[-4, 4], [-4, 4]]) - self.obsvar = np.array([[4]], dtype="float64") - self.real_data = np.array([[-6]], dtype="float64") - self.out = [("f", float)] - self.d = 1 - self.p = 2 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - """ - Wraps the unimodal function - """ - thetas = np.array([theta1, theta2]).reshape((1, 2)) - S = np.array([[1, 0.5], [0.5, 1]]) - f = (thetas @ S) @ thetas.T - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the simulator - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class unidentifiable: - def __init__(self): - - self.data_name = "unidentifiable" - self.thetalimits = np.array([[-8, 8], [-8, 8]]) - self.obsvar = np.array([[1 / 0.01, 0], [0, 1]]) - self.real_data = np.array([[0, 0]], dtype="float64") - self.out = [("f", float, (2,))] - self.d = 2 - self.p = 2 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - f = np.array([theta1, theta2]) - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the unidentifiable function - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class himmelblau: - def __init__(self): - - self.data_name = "himmelblau" - self.thetalimits = np.array([[0, 1], [0, 1]]) - self.truelimits = np.array([[-5, 5], [-5, 5]]) - self.obsvar = np.array([[1]], dtype="float64") - self.real_data = np.array([[1]], dtype="float64") - self.out = [("f", float)] - self.p = 2 - self.d = 1 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - - theta1 = self.truelimits[0][0] + theta1 * ( - self.truelimits[0][1] - self.truelimits[0][0] - ) - theta2 = self.truelimits[1][0] + theta2 * ( - self.truelimits[1][1] - self.truelimits[1][0] - ) - f = (theta1**2 + theta2 - 11) ** 2 + (theta1 + theta2**2 - 7) ** 2 - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the himmelblau function - """ - artificial_time(persis_info, sim_specs) - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class holder: - def __init__(self): - - self.data_name = "holder" - self.thetalimits = np.array([[0, 1], [0, 1]]) - self.truelimits = np.array([[-10, 10], [-10, 10]]) - self.obsvar = np.array([[50]], dtype="float64") - self.real_data = np.array([[-19.208502567767606]], dtype="float64") - self.out = [("f", float)] - self.p = 2 - self.d = 1 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - - theta1 = self.truelimits[0][0] + theta1 * ( - self.truelimits[0][1] - self.truelimits[0][0] - ) - theta2 = self.truelimits[1][0] + theta2 * ( - self.truelimits[1][1] - self.truelimits[1][0] - ) - f = -np.abs( - np.sin(theta1) - * np.cos(theta2) - * np.exp(np.abs(1 - (np.sqrt(theta1**2 + theta2**2) / np.pi))) - ) - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the holder function - """ - artificial_time(persis_info, sim_specs) - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class ackley: - def __init__(self): - - self.data_name = "ackley" - self.thetalimits = np.array([[0, 1], [0, 1]]) - self.truelimits = np.array([[-5, 5], [-5, 5]]) - self.obsvar = np.array([[10]], dtype="float64") - self.real_data = np.array([[0]], dtype="float64") - self.out = [("f", float)] - self.p = 2 - self.d = 1 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - - theta1 = self.truelimits[0][0] + theta1 * ( - self.truelimits[0][1] - self.truelimits[0][0] - ) - theta2 = self.truelimits[1][0] + theta2 * ( - self.truelimits[1][1] - self.truelimits[1][0] - ) - f = ( - -20.0 * np.exp(-0.2 * np.sqrt(0.5 * (theta1**2 + theta2**2))) - - np.exp(0.5 * (np.cos(2 * np.pi * theta1) + np.cos(2 * np.pi * theta2))) - + np.e - + 20 - ) - - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the ackley function - """ - artificial_time(persis_info, sim_specs) - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class easom: - def __init__(self): - - self.data_name = "easom" - self.thetalimits = np.array([[0, 1], [0, 1]]) - self.truelimits = np.array([[-10, 10], [-10, 10]]) - self.obsvar = np.array([[10]], dtype="float64") - self.real_data = np.array([[-1]], dtype="float64") - self.out = [("f", float)] - self.p = 2 - self.d = 1 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - - theta1 = self.truelimits[0][0] + theta1 * ( - self.truelimits[0][1] - self.truelimits[0][0] - ) - theta2 = self.truelimits[1][0] + theta2 * ( - self.truelimits[1][1] - self.truelimits[1][0] - ) - f = ( - -np.cos(theta1) - * np.cos(theta2) - * np.exp(-((theta1 - np.pi) ** 2 + (theta2 - np.pi) ** 2)) - ) - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the easom function - """ - artificial_time(persis_info, sim_specs) - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class sphere: - def __init__(self): - - self.data_name = "sphere" - self.thetalimits = np.array([[0, 1], [0, 1]]) - self.truelimits = np.array([[-5, 5], [-5, 5]]) - self.obsvar = np.array([[10]], dtype="float64") - self.real_data = np.array([[0]], dtype="float64") - self.out = [("f", float)] - self.p = 2 - self.d = 1 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - theta1 = self.truelimits[0][0] + theta1 * ( - self.truelimits[0][1] - self.truelimits[0][0] - ) - theta2 = self.truelimits[1][0] + theta2 * ( - self.truelimits[1][1] - self.truelimits[1][0] - ) - f = theta1**2 + theta2**2 - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the sphere function - """ - artificial_time(persis_info, sim_specs) - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -class matyas: - def __init__(self): - - self.data_name = "matyas" - self.thetalimits = np.array([[0, 1], [0, 1]]) - self.truelimits = np.array([[-10, 10], [-10, 10]]) - self.obsvar = np.array([[10]], dtype="float64") - self.real_data = np.array([[0]], dtype="float64") - self.out = [("f", float)] - self.p = 2 - self.d = 1 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - theta1 = self.truelimits[0][0] + theta1 * ( - self.truelimits[0][1] - self.truelimits[0][0] - ) - theta2 = self.truelimits[1][0] + theta2 * ( - self.truelimits[1][1] - self.truelimits[1][0] - ) - f = 0.26 * (theta1**2 + theta2**2) - 0.48 * theta1 * theta2 - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the matyas function - """ - artificial_time(persis_info, sim_specs) - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info diff --git a/examples/JQT2024/Figure1.py b/examples/JQT2024/Figure1.py deleted file mode 100644 index 92691b8..0000000 --- a/examples/JQT2024/Figure1.py +++ /dev/null @@ -1,194 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments -from PUQ.prior import prior_dist -from plots_design import create_test, samplingdata -from PUQ.surrogate import emulator -from PUQ.surrogatemethods.PCGPexp import postpred -from ptest_funcs import sinfunc -import matplotlib.pyplot as plt - -args = parse_arguments() - -s = 1 -ninit = 10 -nmax = 30 -result = [] - -cls_data = sinfunc() -dt = len(cls_data.true_theta) -cls_data.realdata( - x=np.array([0.1, 0.1, 0.3, 0.3, 0.5, 0.5, 0.7, 0.7, 0.9, 0.9])[:, None], seed=s -) - -prior_xt = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] -) -prior_x = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[0][0]]), b=np.array([cls_data.thetalimits[0][1]]) -) -prior_t = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[1][0]]), b=np.array([cls_data.thetalimits[1][1]]) -) - -priors = {"prior": prior_xt, "priorx": prior_x, "priort": prior_t} - -# # # Create a mesh for test set # # # -xt_test, ftest, ptest, thetamesh, xmesh = create_test(cls_data) -nmesh = len(xmesh) -cls_data_y = sinfunc() -cls_data_y.realdata(x=xmesh, seed=s) -ytest = cls_data_y.real_data - -test_data = { - "theta": xt_test, - "f": ftest, - "p": ptest, - "y": ytest, - "th": thetamesh, - "xmesh": xmesh, - "p_prior": 1, -} - -if __name__ == "__main__": - # # # # # # # # # # # # # # # # # # # # # - al_ceivarx = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivarx", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "theta_torun": None, - "max_evals": nmax, - "is_thetamle": False, - }, - ) - - xt_eivarx = al_ceivarx._info["theta"] - f_eivarx = al_ceivarx._info["f"] - thetamle_eivarx = al_ceivarx._info["thetamle"][-1] - - al_ceivar = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivar", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "theta_torun": None, - "max_evals": nmax, - "is_thetamle": False, - }, - ) - - xt_eivar = al_ceivar._info["theta"] - f_eivar = al_ceivar._info["f"] - thetamle_eivar = al_ceivar._info["thetamle"][-1] - - # LHS - xt_LHS = samplingdata("LHS", nmax - ninit, cls_data, s, prior_xt) - al_LHS = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_LHS, - "bias": False, - "is_thetamle": False, - }, - ) - xt_LHS = al_LHS._info["theta"] - f_LHS = al_LHS._info["f"] - thetamle_LHS = al_LHS._info["thetamle"][-1] - - dataset = [] - - dataset.append({"mle": thetamle_LHS, "f": f_LHS, "xt": xt_LHS, "method": "Alhs"}) - dataset.append( - {"mle": thetamle_eivar, "f": f_eivar, "xt": xt_eivar, "method": "Ap"} - ) - dataset.append( - {"mle": thetamle_eivarx, "f": f_eivarx, "xt": xt_eivarx, "method": "Ay"} - ) - - xt_true = [ - np.concatenate([xc.reshape(1, 1), cls_data.true_theta.reshape(1, 1)], axis=1) - for xc in xmesh - ] - xt_true = np.array([m for mesh in xt_true for m in mesh]) - true_model = cls_data.function(xt_true[:, 0], xt_true[:, 1]) - - x_emu = np.arange(0, 1)[:, None] - - fig, axs = plt.subplots(2, 3, figsize=(15, 7)) - for pid, point in enumerate(dataset): - theta_mle = point["mle"] - f = point["f"] - xt = point["xt"] - - xt_ref = [np.concatenate([xc.reshape(1, 1), theta_mle], axis=1) for xc in xmesh] - xt_ref = np.array([m for mesh in xt_ref for m in mesh]) - - emu = emulator(x_emu, xt, f[None, :], method="PCGPexp") - - # Predictions - emupred = emu.predict(x=x_emu, theta=xt_ref) - predmean = emupred.mean().flatten() - predsd = np.sqrt(emupred.var().flatten()) - - # optional for presentation - ft = 16 - axs[0, pid].plot( - xmesh.flatten(), predmean, color="blue", linestyle="dashed", linewidth=3 - ) - axs[0, pid].fill_between( - xmesh.flatten(), - predmean - predsd, - predmean + predsd, - color="blue", - alpha=0.1, - ) - axs[0, pid].plot( - xmesh.flatten(), true_model.flatten(), color="red", linewidth=3 - ) - axs[0, pid].scatter( - cls_data.x.flatten(), cls_data.real_data, color="black", s=50 - ) - axs[0, pid].set_xlabel(r"$x$", fontsize=ft) - axs[0, pid].set_ylabel(r"$\eta(x, \theta=\pi/5)$", fontsize=ft) - axs[0, pid].tick_params(labelsize=ft) - - # Posterior - pmeanhat, pvarhat = postpred( - emu._info, cls_data.x, xt_test, cls_data.real_data, cls_data.obsvar - ) - axs[1, pid].plot( - thetamesh.flatten(), pmeanhat, color="blue", linestyle="dashed", linewidth=3 - ) - pl = pmeanhat + np.sqrt(pvarhat) - mn = pmeanhat - np.sqrt(pvarhat) - axs[1, pid].fill_between(thetamesh.flatten(), mn, pl, color="blue", alpha=0.2) - axs[1, pid].plot(thetamesh.flatten(), ptest.flatten(), color="red", linewidth=3) - axs[1, pid].set_ylabel(r"$\tilde{p}(\theta|y)$", fontsize=ft) - axs[1, pid].set_xlabel(r"$\theta$", fontsize=ft) - axs[1, pid].tick_params(labelsize=ft) - - -plt.savefig("Figure1.jpg", format="jpeg", bbox_inches="tight", dpi=1000) -plt.show() diff --git a/examples/JQT2024/Figure2.py b/examples/JQT2024/Figure2.py deleted file mode 100644 index 0c11d76..0000000 --- a/examples/JQT2024/Figure2.py +++ /dev/null @@ -1,147 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.prior import prior_dist -from plots_design import create_test -from ptest_funcs import sinfunc -import matplotlib.pyplot as plt -from PUQ.surrogate import emulator -from Figure2support import ceivarfig, ceivarxfig -from PUQ.designmethods.SEQDESsupport import find_mle - - -options = [0, 1] -fig, axs = plt.subplots(2, 3, figsize=(15, 7)) -for o in options: - s = 1 - - cls_data = sinfunc() - dt = len(cls_data.true_theta) - cls_data.realdata( - x=np.array([0.1, 0.1, 0.3, 0.3, 0.5, 0.5, 0.7, 0.7, 0.9, 0.9])[:, None], seed=s - ) - - prior_xt = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - prior_x = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[0][0]]), - b=np.array([cls_data.thetalimits[0][1]]), - ) - prior_t = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[1][0]]), - b=np.array([cls_data.thetalimits[1][1]]), - ) - - priors = {"prior": prior_xt, "priorx": prior_x, "priort": prior_t} - - # # # Create a mesh for test set # # # - xt_test, ftest, ptest, thetamesh, xmesh = create_test(cls_data) - nmesh = len(xmesh) - cls_data_y = sinfunc() - cls_data_y.realdata(x=xmesh, seed=s) - ytest = cls_data_y.real_data - - test_data = { - "theta": xt_test, - "f": ftest, - "p": ptest, - "y": ytest, - "th": thetamesh, - "xmesh": xmesh, - "p_prior": 1, - } - # # # # # # # # # # # # # # # # # # # # # - x_emu = np.arange(0, 1)[:, None] - sinit = 5 - ninit = 10 - nmax = 30 - - # Create initial data - xt = prior_xt.rnd(ninit, sinit) - f = cls_data.function(xt[:, 0], xt[:, 1]) - - if o == 0: - # Acquire new points - for i in range(nmax - ninit): - emu = emulator(x_emu, xt, f[None, :], method="PCGPexp") - - theta_mle = find_mle( - emu, - cls_data.x, - x_emu, - cls_data.real_data, - cls_data.obsvar, - 1, - 1, - cls_data.thetalimits, - is_bias=False, - ) - - xnew = ceivarfig( - 1, - cls_data.x, - cls_data.x, - emu, - xt, - f[None, :], - cls_data.real_data, - cls_data.obsvar, - cls_data.thetalimits, - prior_xt, - prior_t, - thetatest=None, - x_mesh=xmesh, - thetamesh=thetamesh, - posttest=ptest, - type_init=None, - synth_info=cls_data, - theta_mle=theta_mle, - axis=axs, - ) - - xt = np.concatenate((xt, xnew), axis=0) - f = cls_data.function(xt[:, 0], xt[:, 1]) - else: - # Acquire new points - for i in range(nmax - ninit): - emu = emulator(x_emu, xt, f[None, :], method="PCGPexp") - - theta_mle = find_mle( - emu, - cls_data.x, - x_emu, - cls_data.real_data, - cls_data.obsvar, - 1, - 1, - cls_data.thetalimits, - is_bias=False, - ) - - xnew = ceivarxfig( - 1, - cls_data.x, - cls_data.x, - emu, - xt, - f[None, :], - cls_data.real_data, - cls_data.obsvar, - cls_data.thetalimits, - prior_xt, - prior_t, - thetatest=None, - x_mesh=xmesh, - thetamesh=thetamesh, - posttest=ptest, - type_init=None, - synth_info=cls_data, - theta_mle=theta_mle, - axis=axs, - ) - - xt = np.concatenate((xt, xnew), axis=0) - f = cls_data.function(xt[:, 0], xt[:, 1]) - -plt.savefig("Figure2.jpg", format="jpeg", bbox_inches="tight", dpi=1000) -plt.show() diff --git a/examples/JQT2024/Figure2support.py b/examples/JQT2024/Figure2support.py deleted file mode 100644 index e5962c8..0000000 --- a/examples/JQT2024/Figure2support.py +++ /dev/null @@ -1,325 +0,0 @@ -import numpy as np -from PUQ.surrogatemethods.PCGPexp import temp_postphimat, postphimat -from smt.sampling_methods import LHS -from numpy.random import rand -import scipy.stats as sps -import matplotlib.pyplot as plt - - -def ceivarxfig( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - prior_func_t, - thetatest=None, - x_mesh=None, - thetamesh=None, - posttest=None, - type_init=None, - synth_info=None, - theta_mle=None, - axis=None, -): - - ft = 16 - - p = theta.shape[1] - dt = thetamesh.shape[1] - dx = x.shape[1] - type_init = "CMB" - x_emu = np.arange(0, 1)[:, None] - xuniq = np.unique(x, axis=0) - - nx_ref = x_mesh.shape[0] - dx = x_mesh.shape[1] - nt_ref = thetamesh.shape[0] - dt = thetamesh.shape[1] - nf = x.shape[0] - - x_ref = 1 * x_mesh - # nt_ref x d_t - theta_ref = 1 * thetamesh - - # Get estimate for real data at theta_mle for reference x - # nx_ref x (d_x + d_t) - xt_ref = np.concatenate( - (x_ref, np.repeat(theta_mle, nx_ref).reshape(nx_ref, dt)), axis=1 - ) - xt_ref = [np.concatenate([xc.reshape(1, dx), theta_mle], axis=1) for xc in x_ref] - xt_ref = np.array([m for mesh in xt_ref for m in mesh]) - - # 1 x nx_ref - y_ref = emu.predict(x=x_emu, theta=xt_ref).mean() - # y_ref_var = emu.predict(x=x_emu, theta=xt_ref).var() - - # nx_ref x nf - f_temp_rep = np.repeat(obs, nx_ref, axis=0) - # nx_ref x (nf + 1) - f_field_rep = np.concatenate((f_temp_rep, y_ref.T), axis=1) - - xs = [np.concatenate([x, xc.reshape(1, dx)], axis=0) for xc in x_ref] - ts = [np.repeat(theta_mle.reshape(1, dt), nf + 1, axis=0)] - mesh_grid = [np.concatenate([xc, th], axis=1).tolist() for xc in xs for th in ts] - mesh_grid = np.array([m for mesh in mesh_grid for m in mesh]) - - n_x = nf + 1 - - # Construct obsvar - obsvar3D = np.zeros(shape=(nx_ref, n_x, n_x)) - for i in range(nx_ref): - obsvar3D[i, :, :] = np.diag(np.repeat(synth_info.sigma2, n_x)) - - Smat3D, rVh_1_3d, pred_mean = temp_postphimat( - emu._info, n_x, mesh_grid, f_field_rep, obsvar3D - ) - - nmesh = 50 - a = np.arange(nmesh) / nmesh - b = np.arange(nmesh) / nmesh - X, Y = np.meshgrid(a, b) - Z = np.zeros((nmesh, nmesh)) - for i in range(nmesh): - for j in range(nmesh): - xt_cand = np.array([X[i, j], Y[i, j]]).reshape(1, dx + dt) - Z[i, j] = postphimat( - emu._info, - n_x, - mesh_grid, - f_field_rep, - obsvar3D, - xt_cand, - Smat3D, - rVh_1_3d, - pred_mean, - ) - - ids = np.where(Z == Z.max()) - xnew = np.array( - [X[ids[0].flatten(), ids[1].flatten()], Y[ids[0].flatten(), ids[1].flatten()]] - ).reshape(1, dx + dt) - - if theta.shape[0] == 18: - axis[1, 0].contourf(X, Y, Z, cmap="Purples", alpha=1) - axis[1, 0].hlines( - synth_info.true_theta, - 0, - 1, - linestyles="dotted", - linewidth=3, - colors="orange", - ) - for xitem in x: - axis[1, 0].vlines( - xitem, 0, 1, linestyles="dotted", colors="orange", linewidth=3, zorder=1 - ) - axis[1, 0].scatter( - xnew[0, 0], xnew[0, 1], marker="x", c="cyan", s=200, zorder=2, linewidth=3 - ) - axis[1, 0].scatter(theta[0:10, 0], theta[0:10, 1], marker="*", c="blue", s=50) - axis[1, 0].scatter( - theta[10:, 0], theta[10:, 1], marker="+", c="red", s=200, linewidth=3 - ) - axis[1, 0].text(0.8, 0.05, r"$n_t=$" + str(theta.shape[0]), fontsize=ft) - axis[1, 0].set_xlabel(r"$x$", fontsize=ft) - axis[1, 0].set_ylabel(r"$\theta$", fontsize=ft) - axis[1, 0].tick_params(labelsize=ft) - elif theta.shape[0] == 21: - axis[1, 1].contourf(X, Y, Z, cmap="Purples", alpha=1) - axis[1, 1].hlines( - synth_info.true_theta, - 0, - 1, - linestyles="dotted", - linewidth=3, - colors="orange", - ) - for xitem in x: - axis[1, 1].vlines( - xitem, 0, 1, linestyles="dotted", colors="orange", linewidth=3, zorder=1 - ) - axis[1, 1].scatter( - xnew[0, 0], xnew[0, 1], marker="x", c="cyan", s=200, zorder=2, linewidth=3 - ) - axis[1, 1].scatter(theta[0:10, 0], theta[0:10, 1], marker="*", c="blue", s=50) - axis[1, 1].scatter( - theta[10:, 0], theta[10:, 1], marker="+", c="red", s=200, linewidth=3 - ) - axis[1, 1].text(0.8, 0.05, r"$n_t=$" + str(theta.shape[0]), fontsize=ft) - axis[1, 1].set_xlabel(r"$x$", fontsize=ft) - axis[1, 1].set_ylabel(r"$\theta$", fontsize=ft) - axis[1, 1].tick_params(labelsize=ft) - elif theta.shape[0] == 29: - axis[1, 2].contourf(X, Y, Z, cmap="Purples", alpha=1) - axis[1, 2].hlines( - synth_info.true_theta, - 0, - 1, - linestyles="dotted", - linewidth=3, - colors="orange", - ) - for xitem in x: - axis[1, 2].vlines( - xitem, 0, 1, linestyles="dotted", colors="orange", linewidth=3, zorder=1 - ) - axis[1, 2].scatter( - xnew[0, 0], xnew[0, 1], marker="x", c="cyan", s=200, zorder=2, linewidth=3 - ) - axis[1, 2].scatter(theta[0:10, 0], theta[0:10, 1], marker="*", c="blue", s=50) - axis[1, 2].scatter( - theta[10:, 0], theta[10:, 1], marker="+", c="red", s=200, linewidth=3 - ) - axis[1, 2].text(0.8, 0.05, r"$n_t=$" + str(theta.shape[0]), fontsize=ft) - axis[1, 2].set_xlabel(r"$x$", fontsize=ft) - axis[1, 2].set_ylabel(r"$\theta$", fontsize=ft) - axis[1, 2].tick_params(labelsize=ft) - - return xnew - - -def ceivarfig( - n, - x, - real_x, - emu, - theta, - fevals, - obs, - obsvar, - thetalimits, - prior_func, - prior_func_t, - thetatest=None, - x_mesh=None, - thetamesh=None, - posttest=None, - type_init=None, - synth_info=None, - theta_mle=None, - axis=None, -): - - ft = 16 - - p = theta.shape[1] - dt = thetamesh.shape[1] - dx = x.shape[1] - n_x = x.shape[0] - - xuniq = np.unique(x, axis=0) - - xt_ref = np.array([np.concatenate([xc, th]) for th in thetamesh for xc in x]) - - Smat3D, rVh_1_3d, pred_mean = temp_postphimat(emu._info, n_x, xt_ref, obs, obsvar) - - nmesh = 50 - a = np.arange(nmesh) / nmesh - b = np.arange(nmesh) / nmesh - X, Y = np.meshgrid(a, b) - Z = np.zeros((nmesh, nmesh)) - for i in range(nmesh): - for j in range(nmesh): - xt_cand = np.array([X[i, j], Y[i, j]]).reshape(1, dx + dt) - Z[i, j] = postphimat( - emu._info, - n_x, - xt_ref, - obs, - obsvar, - xt_cand, - Smat3D, - rVh_1_3d, - pred_mean, - ) - - ids = np.where(Z == Z.max()) - xnew = np.array( - [X[ids[0].flatten(), ids[1].flatten()], Y[ids[0].flatten(), ids[1].flatten()]] - ).reshape(1, dx + dt) - if theta.shape[0] == 11: - axis[0, 0].contourf(X, Y, Z, cmap="Purples", alpha=1) - axis[0, 0].hlines( - synth_info.true_theta, - 0, - 1, - linestyles="dotted", - linewidth=3, - colors="orange", - ) - for xitem in x: - axis[0, 0].vlines( - xitem, 0, 1, linestyles="dotted", colors="orange", linewidth=3, zorder=1 - ) - - axis[0, 0].scatter( - xnew[0, 0], xnew[0, 1], marker="x", c="cyan", s=200, zorder=2, linewidth=3 - ) - axis[0, 0].scatter(theta[0:10, 0], theta[0:10, 1], marker="*", c="blue", s=50) - axis[0, 0].scatter( - theta[10:, 0], theta[10:, 1], marker="+", c="red", s=200, linewidth=3 - ) - axis[0, 0].text(0.8, 0.05, r"$n_t=$" + str(theta.shape[0]), fontsize=ft) - axis[0, 0].set_xlabel(r"$x$", fontsize=ft) - axis[0, 0].set_ylabel(r"$\theta$", fontsize=ft) - axis[0, 0].tick_params(labelsize=ft) - elif theta.shape[0] == 16: - axis[0, 1].contourf(X, Y, Z, cmap="Purples", alpha=1) - axis[0, 1].hlines( - synth_info.true_theta, - 0, - 1, - linestyles="dotted", - linewidth=3, - colors="orange", - ) - for xitem in x: - axis[0, 1].vlines( - xitem, 0, 1, linestyles="dotted", colors="orange", linewidth=3, zorder=1 - ) - - axis[0, 1].scatter( - xnew[0, 0], xnew[0, 1], marker="x", c="cyan", s=200, zorder=2, linewidth=3 - ) - axis[0, 1].scatter(theta[0:10, 0], theta[0:10, 1], marker="*", c="blue", s=50) - axis[0, 1].scatter( - theta[10:, 0], theta[10:, 1], marker="+", c="red", s=200, linewidth=3 - ) - axis[0, 1].text(0.8, 0.05, r"$n_t=$" + str(theta.shape[0]), fontsize=ft) - axis[0, 1].set_xlabel(r"$x$", fontsize=ft) - axis[0, 1].set_ylabel(r"$\theta$", fontsize=ft) - axis[0, 1].tick_params(labelsize=ft) - elif theta.shape[0] == 29: - axis[0, 2].contourf(X, Y, Z, cmap="Purples", alpha=1) - axis[0, 2].hlines( - synth_info.true_theta, - 0, - 1, - linestyles="dotted", - linewidth=3, - colors="orange", - ) - for xitem in x: - axis[0, 2].vlines( - xitem, 0, 1, linestyles="dotted", colors="orange", linewidth=3, zorder=1 - ) - - axis[0, 2].scatter( - xnew[0, 0], xnew[0, 1], marker="x", c="cyan", s=200, zorder=2, linewidth=3 - ) - axis[0, 2].scatter(theta[0:10, 0], theta[0:10, 1], marker="*", c="blue", s=50) - axis[0, 2].scatter( - theta[10:, 0], theta[10:, 1], marker="+", c="red", s=200, linewidth=3 - ) - axis[0, 2].text(0.8, 0.05, r"$n_t=$" + str(theta.shape[0]), fontsize=ft) - axis[0, 2].set_xlabel(r"$x$", fontsize=ft) - axis[0, 2].set_ylabel(r"$\theta$", fontsize=ft) - axis[0, 2].tick_params(labelsize=ft) - - return xnew diff --git a/examples/JQT2024/Figure3.py b/examples/JQT2024/Figure3.py deleted file mode 100644 index 852c115..0000000 --- a/examples/JQT2024/Figure3.py +++ /dev/null @@ -1,85 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from ptest_funcs import sinfunc - -# optional for presentation -# fig, axs = plt.subplots(1, 2, figsize=(24, 6)) -fig, axs = plt.subplots(1, 2, figsize=(12, 4)) -s = 1 -cls_data = sinfunc() -dt = len(cls_data.true_theta) -cls_data.realdata( - x=np.array([0.1, 0.1, 0.3, 0.3, 0.5, 0.5, 0.7, 0.7, 0.9, 0.9])[:, None], seed=s -) - -th_vec = [np.pi / 7, np.pi / 6, np.pi / 5, np.pi / 4] -thlabel = [7, 6, 5, 4] -x_vec = (np.arange(0, 100, 1) / 100)[:, None] -fvec = np.zeros((len(th_vec), len(x_vec))) -colors = ["blue", "orange", "red", "green", "purple"] -for t_id, t in enumerate(th_vec): - for x_id, x in enumerate(x_vec): - fvec[t_id, x_id] = cls_data.function(x, t)[0] - axs[0].plot( - x_vec, - fvec[t_id, :], - label=r"$\theta=\pi/$" + str(thlabel[t_id]), - color=colors[t_id], - linewidth=3, - ) - -for d_id in range(len(cls_data.x)): - axs[0].scatter( - cls_data.x[d_id, 0], cls_data.real_data[0, d_id], color="black", s=50 - ) -# ft = 16 -ft = 16 -axs[0].set_xlabel(r"$x$", fontsize=ft) -axs[0].set_ylabel(r"$\eta(x, \theta)$", fontsize=ft) -axs[0].set_xticks([0.1, 0.3, 0.5, 0.7, 0.9], [0.1, 0.3, 0.5, 0.7, 0.9]) -axs[0].tick_params(labelsize=ft - 2) -axs[0].legend(bbox_to_anchor=(1.1, -0.2), fontsize=ft - 2, ncol=4) - -s = 1 -cls_data = sinfunc() -dt = len(cls_data.true_theta) -cls_data.realdata( - x=np.array([0.1, 0.1, 0.3, 0.3, 0.5, 0.5, 0.7, 0.7, 0.9, 0.9])[:, None], - seed=s, - isbias=True, -) - -th_vec = [np.pi / 5] -thlabel = [5] -x_vec = (np.arange(0, 100, 1) / 100)[:, None] -fvec = np.zeros((len(th_vec), len(x_vec))) -colors = ["blue", "orange", "red", "green", "purple"] -biasvec = cls_data.bias(x_vec) -for t_id, t in enumerate(th_vec): - for x_id, x in enumerate(x_vec): - fvec[t_id, x_id] = cls_data.function(x, t)[0] - - axs[1].plot( - x_vec, fvec[t_id, :], label=r"$\eta(x, \theta=\pi/5)$", color="red", linewidth=3 - ) - axs[1].plot( - x_vec, - fvec[t_id, :] + biasvec.flatten(), - label=r"$\mathbb{E}[y(x)]$", - color="purple", - linestyle="dashed", - linewidth=3, - ) - -for d_id in range(len(cls_data.x)): - axs[1].scatter( - cls_data.x[d_id, 0], cls_data.real_data[0, d_id], color="black", s=50 - ) -ft = 16 -axs[1].set_xlabel(r"$x$", fontsize=ft) -axs[1].set_xticks([0.1, 0.3, 0.5, 0.7, 0.9], [0.1, 0.3, 0.5, 0.7, 0.9]) -axs[1].tick_params(labelsize=ft - 2) -axs[1].legend(bbox_to_anchor=(0.9, -0.2), fontsize=ft - 2, ncol=2) - -plt.savefig("Figure3.jpg", format="jpeg", bbox_inches="tight", dpi=1000) -plt.show() diff --git a/examples/JQT2024/Figure4.py b/examples/JQT2024/Figure4.py deleted file mode 100644 index 4a5200f..0000000 --- a/examples/JQT2024/Figure4.py +++ /dev/null @@ -1,79 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from ptest_funcs import pritam - -# fig, axs = plt.subplots(1, 2, figsize=(15, 6)) -fig, axs = plt.subplots(1, 2, figsize=(12, 4)) -s = 1 -x = np.linspace(0, 1, 3) -y = np.linspace(0, 1, 3) -xr = np.array([[xx, yy] for xx in x for yy in y]) -xr = np.concatenate((xr, xr)) - -# Data -cls_data = pritam() -cls_data.realdata(xr, seed=s) - -# Model -nmesh = 50 -a = np.arange(nmesh + 1) / nmesh -b = np.arange(nmesh + 1) / nmesh -X, Y = np.meshgrid(a, b) -Z = np.zeros((nmesh + 1, nmesh + 1)) -B = np.zeros((nmesh + 1, nmesh + 1)) -for i in range(nmesh + 1): - for j in range(nmesh + 1): - xt_cand = np.array([X[i, j], Y[i, j]]).reshape(1, 2) - Z[i, j] = cls_data.function(X[i, j], Y[i, j], 0.5) - B[i, j] = cls_data.bias(X[i, j], Y[i, j]) -# fig, ax = plt.subplots() -CS = axs[0].contourf(X, Y, Z, cmap="Purples", alpha=0.75) -fig.colorbar(CS) -for xid1 in range(len(x)): - for xid2 in range(len(y)): - axs[0].scatter( - x[xid1], x[xid2], marker="x", c="black", linewidth=3, s=100, zorder=2 - ) -ft = 16 -axs[0].set_xlim(-0.05, 1.05) -axs[0].set_ylim(-0.05, 1.05) -axs[0].set_xlabel(r"$x_1$", fontsize=ft) -axs[0].set_ylabel(r"$x_2$", fontsize=ft) -axs[0].set_xticks([0, 0.5, 1], [0, 0.5, 1], fontsize=ft - 2) -axs[0].set_yticks([0, 0.5, 1], [0, 0.5, 1], fontsize=ft - 2) -# plt.savefig("Figure4a.png", bbox_inches="tight") -# plt.show() - -# Bias -# CS = plt.contour(X, Y, B, cmap='Purples', alpha=0.75) -# plt.clabel(CS, inline=1, fontsize=14) -# for xid1 in range(len(x)): -# for xid2 in range(len(y)): -# plt.scatter(x[xid1], x[xid2], marker='x', c='black', linewidth=3, s=100, zorder=2) - -# plt.xlim(-0.02, 1.02) -# plt.ylim(-0.02, 1.02) -# plt.xlabel(r'$x_1$', fontsize=20) -# plt.ylabel(r'$x_2$', fontsize=20) -# plt.xticks([0, 0.5, 1], [0, 0.5, 1], fontsize=15) -# plt.yticks([0, 0.5, 1], [0, 0.5, 1], fontsize=15) -# plt.show() - -# Model + Bias -# fig, ax = plt.subplots() -CS = axs[1].contourf(X, Y, Z + B, cmap="Purples", alpha=0.75) -fig.colorbar(CS) -for xid1 in range(len(x)): - for xid2 in range(len(y)): - axs[1].scatter( - x[xid1], x[xid2], marker="x", c="black", linewidth=3, s=100, zorder=2 - ) - -axs[1].set_xlim(-0.05, 1.05) -axs[1].set_ylim(-0.05, 1.05) -axs[1].set_xlabel(r"$x_1$", fontsize=ft) -axs[1].set_ylabel(r"$x_2$", fontsize=ft) -axs[1].set_xticks([0, 0.5, 1], [0, 0.5, 1], fontsize=ft - 2) -axs[1].set_yticks([0, 0.5, 1], [0, 0.5, 1], fontsize=ft - 2) -plt.savefig("Figure4.jpg", format="jpeg", bbox_inches="tight", dpi=1000) -plt.show() diff --git a/examples/JQT2024/Figure5ab_sinf.py b/examples/JQT2024/Figure5ab_sinf.py deleted file mode 100644 index 78fde48..0000000 --- a/examples/JQT2024/Figure5ab_sinf.py +++ /dev/null @@ -1,185 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments, save_output -from PUQ.prior import prior_dist -from plots_design import create_test, add_result, samplingdata, observe_results -from ptest_funcs import sinfunc - -args = parse_arguments() - -ninit = 10 -nmax = 100 -result = [] - -args.seedmin = 0 -args.seedmax = 30 -if __name__ == "__main__": - for s in np.arange(args.seedmin, args.seedmax): - print("Start replication=" + str(s)) - s = int(s) - cls_data = sinfunc() - dt = len(cls_data.true_theta) - cls_data.realdata( - x=np.array([0.1, 0.1, 0.3, 0.3, 0.5, 0.5, 0.7, 0.7, 0.9, 0.9])[:, None], - seed=s, - ) - - prior_xt = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - prior_x = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[0][0]]), - b=np.array([cls_data.thetalimits[0][1]]), - ) - prior_t = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[1][0]]), - b=np.array([cls_data.thetalimits[1][1]]), - ) - - priors = {"prior": prior_xt, "priorx": prior_x, "priort": prior_t} - - # # # Create a mesh for test set # # # - xt_test, ftest, ptest, thetamesh, xmesh = create_test(cls_data) - nmesh = len(xmesh) - ytest = cls_data.function(xmesh, cls_data.true_theta).T - - test_data = { - "theta": xt_test, - "f": ftest, - "p": ptest, - "y": ytest, - "th": thetamesh, - "xmesh": xmesh, - "p_prior": 1, - } - # # # # # # # # # # # # # # # # # # # # # - al_ceivarx = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivarx", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "is_thetamle": False, - }, - ) - - xt_eivarx = al_ceivarx._info["theta"] - f_eivarx = al_ceivarx._info["f"] - thetamle_eivarx = al_ceivarx._info["thetamle"][-1] - - save_output(al_ceivarx, cls_data.data_name, "ceivarx", 2, 1, s) - - res = { - "method": "eivarx", - "repno": s, - "Prediction Error": al_ceivarx._info["TV"], - "Posterior Error": al_ceivarx._info["HD"], - } - result.append(res) - # # # # # # # # # # # # # # # # # # # # # - al_ceivar = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivar", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "is_thetamle": False, - }, - ) - - xt_eivar = al_ceivar._info["theta"] - f_eivar = al_ceivar._info["f"] - thetamle_eivar = al_ceivar._info["thetamle"][-1] - - save_output(al_ceivar, cls_data.data_name, "ceivar", 2, 1, s) - - res = { - "method": "eivar", - "repno": s, - "Prediction Error": al_ceivar._info["TV"], - "Posterior Error": al_ceivar._info["HD"], - } - result.append(res) - - # LHS - xt_LHS = samplingdata("LHS", nmax - ninit, cls_data, s, prior_xt) - al_LHS = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_LHS, - "is_thetamle": False, - }, - ) - xt_LHS = al_LHS._info["theta"] - f_LHS = al_LHS._info["f"] - thetamle_LHS = al_LHS._info["thetamle"][-1] - - save_output(al_LHS, cls_data.data_name, "lhs", 2, 1, s) - - res = { - "method": "lhs", - "repno": s, - "Prediction Error": al_LHS._info["TV"], - "Posterior Error": al_LHS._info["HD"], - } - result.append(res) - - # rnd - xt_RND = samplingdata("Random", nmax - ninit, cls_data, s, prior_xt) - al_RND = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_RND, - "is_thetamle": False, - }, - ) - xt_RND = al_RND._info["theta"] - f_RND = al_RND._info["f"] - thetamle_RND = al_RND._info["thetamle"][-1] - - save_output(al_RND, cls_data.data_name, "rnd", 2, 1, s) - - res = { - "method": "rnd", - "repno": s, - "Prediction Error": al_RND._info["TV"], - "Posterior Error": al_RND._info["HD"], - } - result.append(res) - - print("End replication=" + str(s)) - - method = ["eivarx", "eivar", "lhs", "rnd"] - observe_results(result, method, args.seedmax - args.seedmin, ninit, nmax) diff --git a/examples/JQT2024/Figure5c_sinf.py b/examples/JQT2024/Figure5c_sinf.py deleted file mode 100644 index 815c0e8..0000000 --- a/examples/JQT2024/Figure5c_sinf.py +++ /dev/null @@ -1,184 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.utils import parse_arguments, save_output -from PUQ.prior import prior_dist -from plots_design import create_test, add_result, samplingdata, observe_results -from ptest_funcs import sinfunc - -args = parse_arguments() - - -ninit = 10 -nmax = 100 -result = [] -args.seedmin = 1 -args.seedmax = 2 - -if __name__ == "__main__": - - for s in np.arange(args.seedmin, args.seedmax): - - s = int(s) - bias = True - - cls_data = sinfunc() - dt = len(cls_data.true_theta) - cls_data.realdata( - np.array([0.1, 0.1, 0.3, 0.3, 0.5, 0.5, 0.7, 0.7, 0.9, 0.9])[:, None], - seed=s, - isbias=bias, - ) - - prior_xt = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - prior_x = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[0][0]]), - b=np.array([cls_data.thetalimits[0][1]]), - ) - prior_t = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[1][0]]), - b=np.array([cls_data.thetalimits[1][1]]), - ) - - priors = {"prior": prior_xt, "priorx": prior_x, "priort": prior_t} - - # # # Create a mesh for test set # # # - xt_test, ftest, ptest, thetamesh, xmesh = create_test(cls_data, isbias=bias) - nmesh = len(xmesh) - ytest = (cls_data.function(xmesh, cls_data.true_theta) + cls_data.bias(xmesh)).T - - test_data = { - "theta": xt_test, - "f": ftest, - "p": ptest, - "y": ytest, - "th": thetamesh, - "xmesh": xmesh, - "p_prior": 1, - } - # # # # # # # # # # # # # # # # # # # # # - al_ceivarx = designer( - data_cls=cls_data, - method="SEQDESBIAS", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivarxbias", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "unknowncov": True, - }, - ) - - xt_eivarx = al_ceivarx._info["theta"] - f_eivarx = al_ceivarx._info["f"] - - save_output(al_ceivarx, cls_data.data_name, "ceivarx", 2, 1, s) - - res = { - "method": "ceivarxbias", - "repno": s, - "Prediction Error": al_ceivarx._info["TV"], - "Posterior Error": al_ceivarx._info["HD"], - } - result.append(res) - # # # # # # # # # # # # # # # # # # # # # - al_ceivar = designer( - data_cls=cls_data, - method="SEQDESBIAS", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivarbias", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "unknowncov": True, - }, - ) - - xt_eivar = al_ceivar._info["theta"] - f_eivar = al_ceivar._info["f"] - - save_output(al_ceivar, cls_data.data_name, "ceivar", 2, 1, s) - - res = { - "method": "ceivarbias", - "repno": s, - "Prediction Error": al_ceivar._info["TV"], - "Posterior Error": al_ceivar._info["HD"], - } - result.append(res) - - # LHS - xt_LHS = samplingdata("LHS", nmax - ninit, cls_data, s, prior_xt) - al_LHS = designer( - data_cls=cls_data, - method="SEQDESBIAS", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_LHS, - "unknowncov": True, - }, - ) - xt_LHS = al_LHS._info["theta"] - f_LHS = al_LHS._info["f"] - - save_output(al_LHS, cls_data.data_name, "lhs", 2, 1, s) - - res = { - "method": "lhs", - "repno": s, - "Prediction Error": al_LHS._info["TV"], - "Posterior Error": al_LHS._info["HD"], - } - result.append(res) - - # rnd - xt_RND = samplingdata("Random", nmax - ninit, cls_data, s, prior_xt) - al_RND = designer( - data_cls=cls_data, - method="SEQDESBIAS", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_RND, - "unknowncov": True, - }, - ) - xt_RND = al_RND._info["theta"] - f_RND = al_RND._info["f"] - - save_output(al_RND, cls_data.data_name, "rnd", 2, 1, s) - - res = { - "method": "rnd", - "repno": s, - "Prediction Error": al_RND._info["TV"], - "Posterior Error": al_RND._info["HD"], - } - result.append(res) - - method = ["ceivarxbias", "ceivarbias", "lhs", "rnd"] - observe_results(result, method, args.seedmax - args.seedmin, ninit, nmax) diff --git a/examples/JQT2024/Figure6ab_pritam.py b/examples/JQT2024/Figure6ab_pritam.py deleted file mode 100644 index af79ec6..0000000 --- a/examples/JQT2024/Figure6ab_pritam.py +++ /dev/null @@ -1,182 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.utils import parse_arguments, save_output -from PUQ.prior import prior_dist -from plots_design import create_test_non, add_result, samplingdata, observe_results -from ptest_funcs import pritam - -args = parse_arguments() - - -ninit = 30 -nmax = 180 -result = [] - -args.seedmin = 0 -args.seedmax = 1 - -if __name__ == "__main__": - for s in np.arange(args.seedmin, args.seedmax): - - s = int(s) - - x = np.linspace(0, 1, 3) - y = np.linspace(0, 1, 3) - xr = np.array([[xx, yy] for xx in x for yy in y]) - xr = np.concatenate((xr, xr)) - cls_data = pritam() - cls_data.realdata(xr, seed=s) - - prior_xt = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - prior_x = prior_dist(dist="uniform")( - a=cls_data.thetalimits[0:2, 0], b=cls_data.thetalimits[0:2, 1] - ) - prior_t = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[2][0]]), - b=np.array([cls_data.thetalimits[2][1]]), - ) - - priors = {"prior": prior_xt, "priorx": prior_x, "priort": prior_t} - - xt_test, ftest, ptest, thetamesh, xmesh = create_test_non(cls_data) - ytest = cls_data.function( - xmesh[:, 0], xmesh[:, 1], cls_data.true_theta - ).reshape(1, len(xmesh)) - - test_data = { - "theta": xt_test, - "f": ftest, - "p": ptest, - "y": ytest, - "th": thetamesh, - "xmesh": xmesh, - "p_prior": 1, - } - - al_ceivarx = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivarx", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "is_thetamle": False, - }, - ) - - xt_eivarx = al_ceivarx._info["theta"] - f_eivarx = al_ceivarx._info["f"] - - save_output(al_ceivarx, cls_data.data_name, "ceivarx", 2, 1, s) - - res = { - "method": "eivarx", - "repno": s, - "Prediction Error": al_ceivarx._info["TV"], - "Posterior Error": al_ceivarx._info["HD"], - } - result.append(res) - - # # # # # # # # # # # # # # # # # # # # # - al_ceivar = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivar", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "is_thetamle": False, - }, - ) - - xt_eivar = al_ceivar._info["theta"] - f_eivar = al_ceivar._info["f"] - - save_output(al_ceivar, cls_data.data_name, "ceivar", 2, 1, s) - - res = { - "method": "eivar", - "repno": s, - "Prediction Error": al_ceivar._info["TV"], - "Posterior Error": al_ceivar._info["HD"], - } - result.append(res) - - # LHS - xt_LHS = samplingdata("LHS", nmax - ninit, cls_data, s, prior_xt) - al_LHS = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_LHS, - "is_thetamle": False, - }, - ) - xt_LHS = al_LHS._info["theta"] - f_LHS = al_LHS._info["f"] - - save_output(al_LHS, cls_data.data_name, "lhs", 2, 1, s) - - res = { - "method": "lhs", - "repno": s, - "Prediction Error": al_LHS._info["TV"], - "Posterior Error": al_LHS._info["HD"], - } - result.append(res) - - # rnd - xt_RND = samplingdata("Random", nmax - ninit, cls_data, s, prior_xt) - al_RND = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_RND, - "is_thetamle": False, - }, - ) - xt_RND = al_RND._info["theta"] - f_RND = al_RND._info["f"] - - save_output(al_RND, cls_data.data_name, "rnd", 2, 1, s) - - res = { - "method": "rnd", - "repno": s, - "Prediction Error": al_RND._info["TV"], - "Posterior Error": al_RND._info["HD"], - } - result.append(res) - - method = ["eivarx", "eivar", "lhs", "rnd"] - observe_results(result, method, args.seedmax - args.seedmin, ninit, nmax) diff --git a/examples/JQT2024/Figure6c_pritam.py b/examples/JQT2024/Figure6c_pritam.py deleted file mode 100644 index 8f1e817..0000000 --- a/examples/JQT2024/Figure6c_pritam.py +++ /dev/null @@ -1,185 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.utils import parse_arguments, save_output -from PUQ.prior import prior_dist -from plots_design import create_test_non, add_result, samplingdata, observe_results -from ptest_funcs import pritam - -args = parse_arguments() - - -ninit = 30 -nmax = 80 -result = [] -args.seedmin = 0 -args.seedmax = 1 -for s in np.arange(args.seedmin, args.seedmax): - - s = int(s) - bias = True - - x = np.linspace(0, 1, 3) - y = np.linspace(0, 1, 3) - xr = np.array([[xx, yy] for xx in x for yy in y]) - xr = np.concatenate((xr, xr)) - - cls_data = pritam() - cls_data.realdata(xr, seed=s, isbias=bias) - - prior_xt = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - prior_x = prior_dist(dist="uniform")( - a=cls_data.thetalimits[0:2, 0], b=cls_data.thetalimits[0:2, 1] - ) - prior_t = prior_dist(dist="uniform")( - a=np.array([cls_data.thetalimits[2][0]]), - b=np.array([cls_data.thetalimits[2][1]]), - ) - - priors = {"prior": prior_xt, "priorx": prior_x, "priort": prior_t} - - xt_test, ftest, ptest, thetamesh, xmesh = create_test_non(cls_data, is_bias=bias) - ytest = cls_data.function(xmesh[:, 0], xmesh[:, 1], cls_data.true_theta).reshape( - 1, len(xmesh) - ) + cls_data.bias(xmesh[:, 0], xmesh[:, 1]).reshape(1, len(xmesh)) - - # plt.plot(cls_data.function(cls_data.x[:, 0], cls_data.x[:, 1], cls_data.true_theta) + cls_data.bias(cls_data.x[:, 0], cls_data.x[:, 1])) - # plt.plot(cls_data.real_data.flatten()) - # plt.show() - - test_data = { - "theta": xt_test, - "f": ftest, - "p": ptest, - "y": ytest, - "th": thetamesh, - "xmesh": xmesh, - "p_prior": 1, - } - - al_ceivarx = designer( - data_cls=cls_data, - method="SEQDESBIAS", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivarxbias", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "unknowncov": True, - }, - ) - - xt_eivarx = al_ceivarx._info["theta"] - f_eivarx = al_ceivarx._info["f"] - - save_output(al_ceivarx, cls_data.data_name, "ceivarxbias", 2, 1, s) - - res = { - "method": "ceivarxbias", - "repno": s, - "Prediction Error": al_ceivarx._info["TV"], - "Posterior Error": al_ceivarx._info["HD"], - } - result.append(res) - - # # # # # # # # # # # # # # # # # # # # # - al_ceivar = designer( - data_cls=cls_data, - method="SEQDESBIAS", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivarbias", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "unknowncov": True, - }, - ) - - xt_eivar = al_ceivar._info["theta"] - f_eivar = al_ceivar._info["f"] - - save_output(al_ceivar, cls_data.data_name, "ceivarbias", 2, 1, s) - - res = { - "method": "ceivarbias", - "repno": s, - "Prediction Error": al_ceivar._info["TV"], - "Posterior Error": al_ceivar._info["HD"], - } - result.append(res) - - # LHS - xt_LHS = samplingdata("LHS", 180 - ninit, cls_data, s, prior_xt) - al_LHS = designer( - data_cls=cls_data, - method="SEQDESBIAS", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_LHS, - "unknowncov": True, - }, - ) - xt_LHS = al_LHS._info["theta"] - f_LHS = al_LHS._info["f"] - - save_output(al_LHS, cls_data.data_name, "lhs", 2, 1, s) - - res = { - "method": "lhs", - "repno": s, - "Prediction Error": al_LHS._info["TV"], - "Posterior Error": al_LHS._info["HD"], - } - result.append(res) - - # rnd - xt_RND = samplingdata("Random", 180 - ninit, cls_data, s, prior_xt) - al_RND = designer( - data_cls=cls_data, - method="SEQDESBIAS", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_RND, - "unknowncov": True, - }, - ) - xt_RND = al_RND._info["theta"] - f_RND = al_RND._info["f"] - - save_output(al_RND, cls_data.data_name, "rnd", 2, 1, s) - - res = { - "method": "rnd", - "repno": s, - "Prediction Error": al_RND._info["TV"], - "Posterior Error": al_RND._info["HD"], - } - result.append(res) - -method = ["ceivarxbias", "ceivarbias", "lhs", "rnd"] -observe_results(result, method, args.seedmax - args.seedmin, ninit, nmax) diff --git a/examples/JQT2024/README.rst b/examples/JQT2024/README.rst deleted file mode 100644 index 60d13a6..0000000 --- a/examples/JQT2024/README.rst +++ /dev/null @@ -1,35 +0,0 @@ - -Examples -~~~~~~~~ - -These examples replicate the results from Sürer’s (2024) paper 'Simulation Experiment Design for Calibration via Active Learning'. - - -Instructions for running the illustrative examples -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To replicate Figures~1--4, respectively: - -1) Go to the ``examples/JQT2024`` directory. - -2) Execute the followings from the command line:: - - python3 Figure1.py - python3 Figure2.py - python3 Figure3.py - python3 Figure4.py - -Running each script should not take more than 60 sec. See the figures (png files) saved under the directory. - -Instructions for running one of the prominent empirical results -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Instructions are provided to replicate the first two panels in Figure~6. - -To replicate, execute the following from the command line:: - - python3 Figure5ab_sinf.py - -Running this script takes about 3hrs on a personal Mac laptop. -Once completed, ``Figure5a.png`` and ``Figure5b.png`` are saved under the directory. - diff --git a/examples/JQT2024/highdim_ex.py b/examples/JQT2024/highdim_ex.py deleted file mode 100644 index 0d449b5..0000000 --- a/examples/JQT2024/highdim_ex.py +++ /dev/null @@ -1,200 +0,0 @@ -import numpy as np -from PUQ.design import designer -from PUQ.utils import parse_arguments, save_output -from PUQ.prior import prior_dist -from plots_design import create_test_highdim, add_result, samplingdata -from ptest_funcs import highdim2 - -args = parse_arguments() - -ninit = 50 -nmax = 200 -result = [] - -size_x = 2 - -if __name__ == "__main__": - for s in np.arange(args.seedmin, args.seedmax): - s = int(s) - xr = np.concatenate( - ( - np.repeat(0.5, size_x)[None, :], - np.repeat(0.5, size_x)[None, :], - np.repeat(0.5, size_x)[None, :], - np.repeat(0.5, size_x)[None, :], - ), - axis=0, - ) - # xr = np.concatenate((np.repeat(0.5, size_x)[None, :], np.repeat(0.5, size_x)[None, :], np.repeat(0.5, size_x)[None, :]), axis=0) - # xr = np.concatenate((np.repeat(0.5, size_x)[None, :], np.repeat(0.5, size_x)[None, :]), axis=0) - - cls_data = highdim2() - cls_data.realdata(xr, seed=s) - print(cls_data.sigma2) - - prior_xt = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] - ) - prior_x = prior_dist(dist="uniform")( - a=cls_data.thetalimits[0:size_x, 0], b=cls_data.thetalimits[0:size_x, 1] - ) - prior_t = prior_dist(dist="uniform")( - a=cls_data.thetalimits[size_x:, 0], b=cls_data.thetalimits[size_x:, 1] - ) - - priors = {"prior": prior_xt, "priorx": prior_x, "priort": prior_t} - - xt_test, ftest, ptest, thetamesh, xmesh = create_test_highdim(cls_data) - - if size_x == 2: - ytest = cls_data.function( - xmesh[:, 0], - xmesh[:, 1], - cls_data.true_theta[0], - cls_data.true_theta[1], - cls_data.true_theta[2], - cls_data.true_theta[3], - cls_data.true_theta[4], - cls_data.true_theta[5], - cls_data.true_theta[6], - cls_data.true_theta[7], - cls_data.true_theta[8], - cls_data.true_theta[9], - ).reshape(1, len(xmesh)) - elif size_x == 6: - ytest = cls_data.function( - xmesh[:, 0], - xmesh[:, 1], - xmesh[:, 2], - xmesh[:, 3], - xmesh[:, 4], - xmesh[:, 5], - cls_data.true_theta[0], - cls_data.true_theta[1], - cls_data.true_theta[2], - cls_data.true_theta[3], - cls_data.true_theta[4], - cls_data.true_theta[5], - ).reshape(1, len(xmesh)) - elif size_x == 10: - ytest = cls_data.function( - xmesh[:, 0], - xmesh[:, 1], - xmesh[:, 2], - xmesh[:, 3], - xmesh[:, 4], - xmesh[:, 5], - xmesh[:, 6], - xmesh[:, 7], - xmesh[:, 8], - xmesh[:, 9], - cls_data.true_theta[0], - cls_data.true_theta[1], - ).reshape(1, len(xmesh)) - - test_data = { - "theta": xt_test, - "f": ftest, - "p": ptest, - "y": ytest, - "th": thetamesh, - "xmesh": xmesh, - "p_prior": 1, - } - - al_imspe = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "imspe", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "is_thetamle": False, - }, - ) - - save_output(al_imspe, cls_data.data_name, "imspe", 2, 1, s) - - al_ceivar = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivar", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "is_thetamle": False, - }, - ) - - save_output(al_ceivar, cls_data.data_name, "ceivar", 2, 1, s) - - al_ceivarx = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "ceivarx", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "is_thetamle": False, - }, - ) - - save_output(al_ceivarx, cls_data.data_name, "ceivarx", 2, 1, s) - - # LHS - xt_LHS = samplingdata("LHS", nmax - ninit, cls_data, s, prior_xt) - al_LHS = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": None, - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": xt_LHS, - "is_thetamle": False, - }, - ) - - save_output(al_LHS, cls_data.data_name, "lhs", 2, 1, s) - - al_maxvar = designer( - data_cls=cls_data, - method="SEQDES", - args={ - "mini_batch": 1, - "n_init_thetas": ninit, - "nworkers": 2, - "AL": "maxvar", - "seed_n0": s, - "prior": priors, - "data_test": test_data, - "max_evals": nmax, - "theta_torun": None, - "is_thetamle": False, - }, - ) - - save_output(al_maxvar, cls_data.data_name, "maxvar", 2, 1, s) diff --git a/examples/JQT2024/plot_reps.py b/examples/JQT2024/plot_reps.py deleted file mode 100644 index 22111ac..0000000 --- a/examples/JQT2024/plot_reps.py +++ /dev/null @@ -1,115 +0,0 @@ -from PUQ.utils import parse_arguments, save_output, read_output -import matplotlib.pyplot as plt -import matplotlib -import numpy as np - - -def plotresult(path, out, ex_name, w, b, rep, method, n0, nf): - - HDlist = [] - TVlist = [] - timelist = [] - for i in range(0, rep): - design_saved = read_output("", ex_name, method, w, b, i) - - TV = design_saved._info["TV"] - HD = design_saved._info["HD"] - - TVlist.append(TV[n0:nf]) - HDlist.append(HD[n0:nf]) - - avgTV = np.mean(np.array(TVlist), 0) - sdTV = np.std(np.array(TVlist), 0) - avgHD = np.mean(np.array(HDlist), 0) - sdHD = np.std(np.array(HDlist), 0) - - return avgHD, sdHD, avgTV, sdTV - - -def plot_aggregated(example_name="sinfunc", is_bias=False, rep=30): - - # choose either 'pritam' or 'sinfunc' - clist = ["b", "r", "g", "m", "y", "c", "pink", "purple"] - mlist = ["P", "p", "*", "o", "s", "h"] - linelist = ["-", "--", "-.", ":", "-.", ":"] - labelsb = [ - r"$\mathcal{A}^y$", - r"$\mathcal{A}^p$", - r"$\mathcal{A}^{lhs}$", - r"$\mathcal{A}^{rnd}$", - ] - batch = 1 - worker = 2 - fonts = 18 - # path = '/Users/ozgesurer/Desktop/des_examples/newPUQ/examples/' - # example_name = 'pritam' - # is_bias = True - if example_name == "pritam": - n0, nf = 30, 180 - if is_bias: - outs = "pritam_bias" - method = ["ceivarxbias", "ceivarbias", "lhs", "rnd"] - else: - outs = "pritam" - method = ["ceivarx", "ceivar", "lhs", "rnd"] - - elif example_name == "sinfunc": - n0, nf = 10, 100 - if is_bias: - outs = "sinf_bias" - method = ["ceivarx", "ceivar", "lhs", "rnd"] - else: - outs = "sinf" - method = ["ceivarx", "ceivar", "lhs", "rnd"] - - for metric in ["TV", "HD"]: - fig, axes = plt.subplots(1, 1, figsize=(6, 5)) - plt.rcParams["figure.autolayout"] = True - for mid, m in enumerate(method): - avgPOST, sdPOST, avgPRED, sdPRED = plotresult( - None, outs, example_name, worker, batch, rep, m, n0=n0, nf=nf - ) - if metric == "TV": - axes.plot( - np.arange(len(avgPRED)), - avgPRED, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - linewidth=4, - ) - plt.fill_between( - np.arange(len(avgPRED)), - avgPRED - 1.96 * sdPRED / rep, - avgPRED + 1.96 * sdPRED / rep, - color=clist[mid], - alpha=0.1, - ) - elif metric == "HD": - axes.plot( - np.arange(len(avgPOST)), - avgPOST, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - linewidth=4, - ) - plt.fill_between( - np.arange(len(avgPOST)), - avgPOST - 1.96 * sdPOST / rep, - avgPOST + 1.96 * sdPOST / rep, - color=clist[mid], - alpha=0.1, - ) - axes.set_yscale("log") - # axes.set_xscale('log') - axes.set_xlabel("# of parameters", fontsize=fonts) - - if metric == "TV": - axes.set_ylabel(r"${\rm MAD}^y$", fontsize=fonts) - elif metric == "HD": - axes.set_ylabel(r"${\rm MAD}^p$", fontsize=fonts) - axes.tick_params(axis="both", which="major", labelsize=fonts - 5) - - axes.legend(bbox_to_anchor=(1, -0.2), ncol=4, fontsize=fonts, handletextpad=0.1) - plt.show() diff --git a/examples/JQT2024/plots_design.py b/examples/JQT2024/plots_design.py deleted file mode 100644 index 39ba87c..0000000 --- a/examples/JQT2024/plots_design.py +++ /dev/null @@ -1,366 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -import scipy.stats as sps -from smt.sampling_methods import LHS - - -def plot_EIVAR(xt, cls_data, ninit, xlim1=0, xlim2=1): - - plt.scatter( - cls_data.x, - np.repeat(cls_data.true_theta, len(cls_data.x)), - marker="o", - color="black", - ) - plt.scatter(xt[0:ninit, 0], xt[0:ninit, 1], marker="*", color="blue") - plt.scatter(xt[:, 0][ninit:], xt[:, 1][ninit:], marker="+", color="red") - plt.axhline(y=cls_data.true_theta, color="green") - plt.xlabel("x") - plt.ylabel(r"$\theta$") - plt.show() - - plt.hist(xt[:, 1][ninit:]) - plt.axvline(x=cls_data.true_theta, color="r") - # plt.ylim(0, 1) - plt.xlim(0, 1) - plt.xlabel(r"$\theta$") - plt.show() - - plt.hist(xt[:, 0][ninit:]) - plt.xlabel(r"x") - plt.xlim(xlim1, xlim2) - plt.show() - - -def plot_des(des, xt, n0, cls_data): - xdes = np.array([e["x"] for e in des]) - fdes = np.array([e["feval"][0] for e in des]).T - xu_des, xcount = np.unique(xdes, return_counts=True) - repeatth = np.repeat(cls_data.true_theta, len(xu_des)) - for label, x_count, y_count in zip(xcount, xu_des, repeatth): - plt.annotate(label, xy=(x_count, y_count), xytext=(x_count, y_count)) - plt.scatter(xt[0:n0, 0], xt[0:n0, 1], marker="*", color="blue") - plt.scatter(xt[:, 0][n0:], xt[:, 1][n0:], marker="+", color="red") - plt.axhline(y=cls_data.true_theta, color="black") - plt.xlabel("x") - plt.ylabel(r"$\theta$") - plt.show() - - -def plot_des_pri(xt, cls_data, ninit, nmax): - xacq = xt[ninit:nmax, 0:2] - tacq = xt[ninit:nmax, 2] - - plt.hist(tacq) - plt.axvline(x=cls_data.true_theta, color="r") - plt.xlabel(r"$\theta$") - plt.xlim(0, 1) - plt.show() - - unq, cnt = np.unique(xacq, return_counts=True, axis=0) - plt.scatter(unq[:, 0], unq[:, 1]) - for label, x_count, y_count in zip(cnt, unq[:, 0], unq[:, 1]): - plt.annotate( - label, xy=(x_count, y_count), xytext=(5, -5), textcoords="offset points" - ) - plt.show() - - -def plot_LHS(xt, cls_data): - plt.scatter( - cls_data.x, - np.repeat(cls_data.true_theta, len(cls_data.x)), - marker="o", - color="black", - ) - plt.scatter(xt[:, 0], xt[:, 1], marker="*", color="blue") - plt.axhline(y=cls_data.true_theta, color="green") - plt.xlabel("x") - plt.ylabel(r"$\theta$") - plt.show() - - -def plot_post(theta, phat, ptest, phatvar): - if theta.shape[1] == 1: - plt.plot(theta.flatten(), phat, c="blue", linestyle="dashed") - plt.plot(theta.flatten(), ptest, c="black") - plt.fill_between( - theta.flatten(), phat - np.sqrt(phatvar), phat + np.sqrt(phatvar), alpha=0.2 - ) - plt.show() - else: - plt.scatter(theta[:, 0], theta[:, 1], c=phat) - plt.show() - - -def obsdata(cls_data, is_bias): - th_vec = [0.3, 0.4, 0.5, 0.6, 0.7] - x_vec = (np.arange(0, 100, 1) / 100)[:, None] - fvec = np.zeros((len(th_vec), len(x_vec))) - colors = ["blue", "orange", "green", "red", "purple"] - for t_id, t in enumerate(th_vec): - for x_id, x in enumerate(x_vec): - fvec[t_id, x_id] = cls_data.function(x, t) - plt.plot(x_vec, fvec[t_id, :], label=r"$\theta=$" + str(t), color=colors[t_id]) - - fvec = np.zeros(len(x_vec)) - for x_id, x in enumerate(x_vec): - fvec[x_id] = cls_data.function(x[0], cls_data.true_theta[0]) - - if is_bias: - fvec += cls_data.bias(x_vec).flatten() - plt.plot(x_vec, fvec) - - for d_id in range(cls_data.real_data.shape[1]): - plt.scatter(cls_data.x[d_id, 0], cls_data.real_data[0, d_id], color="black") - plt.xlabel("x") - plt.legend() - plt.show() - - -def create_test(cls_data, isbias=False): - thetamesh = np.linspace( - cls_data.thetalimits[1][0], cls_data.thetalimits[1][1], 100 - )[:, None] - xmesh = np.linspace(cls_data.thetalimits[0][0], cls_data.thetalimits[0][1], 100)[ - :, None - ] - - n_t = thetamesh.shape[0] - n_x = xmesh.shape[0] - d = len(cls_data.x) - if cls_data.nodata: - thetatest, ftest, ptest = None, None, None - else: - # xdesign_vec = np.tile(cls_data.x.flatten(), n_t) - # thetatest = np.concatenate((xdesign_vec[:, None], np.repeat(thetamesh, d)[:, None]), axis=1) - xt_test = np.array( - [np.concatenate([xc, th]) for th in thetamesh for xc in cls_data.x] - ) - ftest = np.zeros((n_t, d)) - for t_id in range(n_t): - for x_id in range(d): - ftest[t_id, x_id] = cls_data.function( - cls_data.x[x_id, 0], thetamesh[t_id, 0] - ) - - ptest = np.zeros(n_t) - for i in range(n_t): - meanval = ftest[i, :] - if isbias: - meanval += cls_data.bias(cls_data.x).flatten() - rnd = sps.multivariate_normal(mean=meanval, cov=cls_data.obsvar) - ptest[i] = rnd.pdf(cls_data.real_data) - - return xt_test, ftest, ptest, thetamesh, xmesh - - -def create_test_non(cls_data, is_bias=False): - n_t = 100 - n_x = cls_data.x.shape[0] - - thetamesh = np.linspace( - cls_data.thetalimits[2][0], cls_data.thetalimits[2][1], n_t - )[:, None] - - xt_test = np.array( - [np.concatenate([xc, th]) for th in thetamesh for xc in cls_data.x] - ) - ftest = np.zeros((n_t, n_x)) - for j in range(n_t): - for i in range(n_x): - ftest[j, i] = cls_data.function( - cls_data.x[i, 0], cls_data.x[i, 1], thetamesh[j, 0] - ) - - if is_bias: - biastrue = cls_data.bias(cls_data.x[:, 0], cls_data.x[:, 1]) - ptest = np.zeros(n_t) - for j in range(n_t): - rnd = sps.multivariate_normal( - mean=ftest[j, :] + biastrue, cov=cls_data.obsvar - ) - ptest[j] = rnd.pdf(cls_data.real_data) - else: - ptest = np.zeros(n_t) - for j in range(n_t): - rnd = sps.multivariate_normal(mean=ftest[j, :], cov=cls_data.obsvar) - ptest[j] = rnd.pdf(cls_data.real_data) - - x1 = np.linspace(cls_data.thetalimits[0][0], cls_data.thetalimits[0][1], 20) - x2 = np.linspace(cls_data.thetalimits[1][0], cls_data.thetalimits[1][1], 20) - X1, X2 = np.meshgrid(x1, x2) - xmesh = np.vstack([X1.ravel(), X2.ravel()]).T - - return xt_test, ftest, ptest, thetamesh, xmesh - - -def add_result(method_name, phat, ptest, yhat, ytest, s): - rep = {} - rep["method"] = method_name - rep["Posterior Error"] = np.mean(np.abs(phat - ptest)) - rep["Prediction Error"] = np.mean(np.abs(yhat - ytest)) - rep["repno"] = s - return rep - - -def samplingdata(typesampling, nmax, cls_data, seed, prior_xt): - - if typesampling == "LHS": - sampling = LHS(xlimits=cls_data.thetalimits, random_state=seed) - xt = sampling(nmax) - elif typesampling == "Random": - xt = prior_xt.rnd(nmax, seed=seed) - - return xt - - -def observe_results(result, method, rep, ninit, nmax): - - clist = ["b", "r", "g", "m"] - mlist = ["P", "p", "*", "o"] - linelist = ["-", "--", "-.", ":"] - labelsb = [ - r"$\mathcal{A}^y$", - r"$\mathcal{A}^p$", - r"$\mathcal{A}^{lhs}$", - r"$\mathcal{A}^{rnd}$", - ] - - fonts = 18 - for metric in ["TV", "HD"]: - fig, axes = plt.subplots(1, 1, figsize=(6, 5)) - plt.rcParams["figure.autolayout"] = True - for mid, m in enumerate(method): - if metric == "TV": - p = np.array( - [ - r["Prediction Error"][ninit:nmax] - for r in result - if r["method"] == m - ] - ) - meanerror = np.mean(p, axis=0) - sderror = np.std(p, axis=0) - axes.plot( - np.arange(len(meanerror)), - meanerror, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - linewidth=4, - ) - plt.fill_between( - np.arange(len(meanerror)), - meanerror - 1.96 * sderror / rep, - meanerror + 1.96 * sderror / rep, - color=clist[mid], - alpha=0.1, - ) - elif metric == "HD": - p = np.array( - [ - r["Posterior Error"][ninit:nmax] - for r in result - if r["method"] == m - ] - ) - meanerror = np.mean(p, axis=0) - sderror = np.std(p, axis=0) - axes.plot( - np.arange(len(meanerror)), - meanerror, - label=labelsb[mid], - color=clist[mid], - linestyle=linelist[mid], - linewidth=4, - ) - plt.fill_between( - np.arange(len(meanerror)), - meanerror - 1.96 * sderror / rep, - meanerror + 1.96 * sderror / rep, - color=clist[mid], - alpha=0.1, - ) - axes.set_yscale("log") - axes.set_xlabel("# of simulation evals", fontsize=fonts) - - if metric == "TV": - axes.set_ylabel(r"${\rm MAD}^y$", fontsize=fonts) - elif metric == "HD": - axes.set_ylabel(r"${\rm MAD}^p$", fontsize=fonts) - axes.tick_params(axis="both", which="major", labelsize=fonts - 5) - - axes.legend( - bbox_to_anchor=(1.1, -0.2), ncol=4, fontsize=fonts, handletextpad=0.1 - ) - plt.show() - - -def create_test_highdim(cls_data, is_bias=False): - n_t = 1500 - n_x = cls_data.x.shape[0] - d_x = cls_data.x.shape[1] - sampling = LHS(xlimits=cls_data.thetalimits[d_x:, :], random_state=0) - thetamesh = sampling(n_t) - - xt_test = np.array( - [np.concatenate([xc, th]) for th in thetamesh for xc in cls_data.x] - ) - ftest = np.zeros((n_t, n_x)) - for j in range(n_t): - for i in range(n_x): - if d_x == 2: - ftest[j, i] = cls_data.function( - cls_data.x[i, 0], - cls_data.x[i, 1], - thetamesh[j, 0], - thetamesh[j, 1], - thetamesh[j, 2], - thetamesh[j, 3], - thetamesh[j, 4], - thetamesh[j, 5], - thetamesh[j, 6], - thetamesh[j, 7], - thetamesh[j, 8], - thetamesh[j, 9], - ) - elif d_x == 6: - ftest[j, i] = cls_data.function( - cls_data.x[i, 0], - cls_data.x[i, 1], - cls_data.x[i, 2], - cls_data.x[i, 3], - cls_data.x[i, 4], - cls_data.x[i, 5], - thetamesh[j, 0], - thetamesh[j, 1], - thetamesh[j, 2], - thetamesh[j, 3], - thetamesh[j, 4], - thetamesh[j, 5], - ) - elif d_x == 10: - ftest[j, i] = cls_data.function( - cls_data.x[i, 0], - cls_data.x[i, 1], - cls_data.x[i, 2], - cls_data.x[i, 3], - cls_data.x[i, 4], - cls_data.x[i, 5], - cls_data.x[i, 6], - cls_data.x[i, 7], - cls_data.x[i, 8], - cls_data.x[i, 9], - thetamesh[j, 0], - thetamesh[j, 1], - ) - - ptest = np.zeros(n_t) - for j in range(n_t): - rnd = sps.multivariate_normal(mean=ftest[j, :], cov=cls_data.obsvar) - ptest[j] = rnd.pdf(cls_data.real_data) - sampling = LHS(xlimits=cls_data.thetalimits[0:d_x, :], random_state=0) - xmesh = sampling(1500) - - return xt_test, ftest, ptest, thetamesh, xmesh diff --git a/examples/README.rst b/examples/README.rst index 97175ca..5f777db 100644 --- a/examples/README.rst +++ b/examples/README.rst @@ -6,10 +6,14 @@ We provide a collection of examples in this directory to demonstrate the usage o The available examples include: -* `Technometrics2024 `_ : These examples replicate the results from the paper `Sequential Bayesian Experimental Design for Calibration of Expensive Simulation Models `_ by Sürer, Plumlee, and Wild (2024). +* `Example1 `_ : These examples replicate the results from the paper `Sequential Bayesian Experimental Design for Calibration of Expensive Simulation Models `_ by Sürer, Plumlee, and Wild (2024). -* `JQT2024 `_ : These examples reproduce the results from Sürer’s (2024) paper `Simulation Experiment Design for Calibration via Active Learning `_ . +* `Example2 `_ : These examples reproduce the results from Sürer’s (2024) paper `Simulation Experiment Design for Calibration via Active Learning `_ . -* `IJOC2024+ `_ : These examples replicate results from the paper 'Performance Analysis of Sequential Experimental Design for Calibration in Parallel Computing Environments' by Sürer and Wild (2024). +* `Example3 `_ : These examples replicate results from the paper `An Active Learning Performance Model for Parallel Bayesian Calibration of Expensive Simulations `_ by Sürer and Wild (2024). + +* `Example4 `_ : These examples replicate the results from the paper `Batch Sequential Experimental Design for Calibration of Stochastic Simulation Models `_ by Sürer (2025). + +* `Example5 `_ : These examples replicate the results from the paper `Active Learning for Data-Efficient Calibration of Stochastic Simulation Models` by Sürer (2025). * `fresco_example `_ : This example showcases the application of PUQ with the physics reaction code `fresco`. diff --git a/examples/Technometrics2024/README.rst b/examples/Technometrics2024/README.rst deleted file mode 100644 index dca9da5..0000000 --- a/examples/Technometrics2024/README.rst +++ /dev/null @@ -1,52 +0,0 @@ - -Examples -~~~~~~~~ - -These examples replicate the results presented in the paper titled 'Sequential Bayesian -Experimental Design for Calibration of Expensive Simulation Models' by Sürer, Plumlee, and Wild (2024). - -**Instructions for running the illustrative examples** - -To replicate Figures~1--3, respectively: - -1) Go to the ``examples/Technometrics2024`` directory. - -2) Execute the followings from the command line:: - - python3 figure1b.py - python3 figure2.py - python3 figure3.py - -Running each script should not take more than 60 sec. See the figures (png files) saved under the directory. - -**Instructions for running the prominent empirical results** - -Instructions are provided to replicate each panel in Figure~6. - -To replicate the upper-left panel (banana function), execute the following from the command line:: - - python3 figure6.py -funcname banana - -Running this script takes about 2.5hrs on a personal Mac laptop. -Once completed, ``Figure6_banana.png`` is saved under the directory. - -To replicate the upper-right panel (bimodal function), execute the following from the command line:: - - python3 figure6.py -funcname bimodal - -Running this script takes about 2.5hrs on a personal Mac laptop. -Once completed, ``Figure6_bimodal.png`` is saved under the directory. - -To replicate the lower-left panel (unimodal function), execute the following from the command line:: - - python3 figure6.py -funcname unimodal - -Running this script takes about 2hr on a personal Mac laptop. -Once completed, ``Figure6_unimodal.png`` is saved under the directory. - -To replicate the lower-right panel (unidentifiable function), execute the following from the command line:: - - python3 figure6.py -funcname unidentifiable - -Running this script takes about 2.5hrs on a personal Mac laptop. -Once completed, ``Figure6_unidentifiable.png`` is saved under the directory. diff --git a/examples/Technometrics2024/figure1b.py b/examples/Technometrics2024/figure1b.py deleted file mode 100644 index 017c22b..0000000 --- a/examples/Technometrics2024/figure1b.py +++ /dev/null @@ -1,114 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -import scipy.stats as sps -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments -from PUQ.prior import prior_dist - - -class unimodal: - def __init__(self): - self.data_name = "unimodal" - self.thetalimits = np.array([[-4, 4], [-4, 4]]) - self.obsvar = np.array([[4]], dtype="float64") - self.real_data = np.array([[-6]], dtype="float64") - self.out = [("f", float)] - self.d = 1 - self.p = 2 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - """ - Wraps the unimodal function - """ - thetas = np.array([theta1, theta2]).reshape((1, 2)) - S = np.array([[1, 0.5], [0.5, 1]]) - f = (thetas @ S) @ thetas.T - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the simulator - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -if __name__ == "__main__": - args = parse_arguments() - cls_unimodal = unimodal() - - # # # Create a mesh for test set # # # - xpl = np.linspace( - cls_unimodal.thetalimits[0][0], cls_unimodal.thetalimits[0][1], 50 - ) - ypl = np.linspace( - cls_unimodal.thetalimits[1][0], cls_unimodal.thetalimits[1][1], 50 - ) - Xpl, Ypl = np.meshgrid(xpl, ypl) - th = np.vstack([Xpl.ravel(), Ypl.ravel()]) - setattr(cls_unimodal, "theta", th.T) - - al_unimodal_test = designer( - data_cls=cls_unimodal, - method="SEQUNIFORM", - args={ - "mini_batch": 4, - "n_init_thetas": 10, - "nworkers": 5, - "max_evals": th.shape[1], - }, - ) - - ftest = al_unimodal_test._info["f"] - thetatest = al_unimodal_test._info["theta"] - ptest = sps.norm.pdf( - cls_unimodal.real_data - ftest, 0, np.sqrt(cls_unimodal.obsvar) - ) - - test_data = {"theta": thetatest, "f": ftest, "p": ptest, "p_prior": 1} - # # # # # # # # # # # # # # # # # # # # # - prior_func = prior_dist(dist="uniform")( - a=cls_unimodal.thetalimits[:, 0], b=cls_unimodal.thetalimits[:, 1] - ) - - al_unimodal = designer( - data_cls=cls_unimodal, - method="SEQCAL", - args={ - "mini_batch": 1, - "n_init_thetas": 10, - "nworkers": 2, - "AL": "eivar", - "seed_n0": 1, - "prior": prior_func, - "data_test": test_data, - "max_evals": 60, - "type_init": None, - }, - ) - - theta_al = al_unimodal._info["theta"] - TV = al_unimodal._info["TV"] - HD = al_unimodal._info["HD"] - - print(theta_al) - fig, ax = plt.subplots() - cp = ax.contour(Xpl, Ypl, ptest.reshape(50, 50), 20, cmap="RdGy") - ax.scatter(theta_al[10:, 0], theta_al[10:, 1], c="black", marker="+", zorder=2) - ax.scatter( - theta_al[0:10, 0], - theta_al[0:10, 1], - zorder=2, - marker="o", - facecolors="none", - edgecolors="blue", - ) - ax.set_xlabel(r"$\theta_1$", fontsize=16) - ax.set_ylabel(r"$\theta_2$", fontsize=16) - ax.tick_params(axis="both", labelsize=16) - plt.savefig("Figure1b.png", bbox_inches="tight") diff --git a/examples/Technometrics2024/figure3.py b/examples/Technometrics2024/figure3.py deleted file mode 100644 index eb10fdd..0000000 --- a/examples/Technometrics2024/figure3.py +++ /dev/null @@ -1,140 +0,0 @@ -import scipy.stats as sps -import numpy as np -import matplotlib.pyplot as plt -from PUQ.surrogate import emulator -from PUQ.posterior import posterior -from PUQ.designmethods.gen_funcs.acquisition_funcs_support import ( - get_emuvar, - compute_eivar_fig, -) - - -class sinlinear: - def __init__(self): - self.data_name = "sinlinear" - self.thetalimits = np.array([[-10, 10]]) - self.obsvar = np.array([[1]], dtype="float64") - self.real_data = np.array([[0]], dtype="float64") - self.out = [("f", float)] - self.p = 1 - self.d = 1 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta): - f = np.sin(theta) + 0.1 * theta - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the sin() function - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - theta = H["thetas"][0] - H_o["f"] = function(theta) - - return H_o, persis_info - - -if __name__ == "__main__": - cls_sinlin = sinlinear() - - # Generate training data - theta = np.array([-10, -8.7, -7.5, -5, -2.5, -1, 1, 2.5, 5, 7.5, 8.7, 10])[:, None] - f = cls_sinlin.function(theta) - - for i in range(3): - # Fit an emulator - emu = emulator(cls_sinlin.x, theta, f, method="PCGP") - - post = posterior(data_cls=cls_sinlin, emulator=emu) - - # Generate test data - thetatest = np.arange(-10, 10, 0.0025)[:, None] - ftest = cls_sinlin.function(thetatest) - ptest = sps.norm.pdf(cls_sinlin.real_data, ftest, np.sqrt(cls_sinlin.obsvar)) - - # Predict via the emulator - emupred_test = emu.predict(x=cls_sinlin.x, theta=thetatest) - emupred_tr = emu.predict(x=cls_sinlin.x, theta=theta) - - posttesthat, posttestvar = post.predict(thetatest) - posttrhat, posttrvar = post.predict(theta) - - clist = np.arange(-10, 10.01, 0.1)[:, None] - - def compute_eivar_full(clist, theta, theta_test, emu): - real_x = cls_sinlin.real_x - obs = cls_sinlin.real_data - obsvar = cls_sinlin.obsvar - obsvar3d = obsvar.reshape(1, 1, 1) - d_real = real_x.shape[0] - x = cls_sinlin.x - emupred_test = emu.predict(x=cls_sinlin.x, theta=theta_test) - emumean = emupred_test.mean() - emuvar, is_cov = get_emuvar(emupred_test) - emumeanT = emumean.T - emuvarT = emuvar.transpose(1, 0, 2) - var_obsvar1 = emuvarT + obsvar3d - var_obsvar2 = emuvarT + 0.5 * obsvar3d - diags = np.diag(obsvar[real_x, real_x.T]) - coef = (2**d_real) * (np.sqrt(np.pi) ** d_real) * np.sqrt(np.prod(diags)) - - # Get the n_ref x d x d x n_cand phi matrix - emuphi4d = emu.acquisition(x=x, theta1=theta_test, theta2=clist) - acq_func = [] - - # Pass over all the candidates - for c_id in range(len(clist)): - posteivar = compute_eivar_fig( - obsvar, - var_obsvar2[:, real_x, real_x.T], - var_obsvar1[:, real_x, real_x.T], - emuphi4d[:, real_x, real_x.T, c_id], - emumeanT[:, real_x.flatten()], - emuvar[real_x, :, real_x.T], - obs, - is_cov, - ) - acq_func.append(posteivar) - - return acq_func - - acq = compute_eivar_full(clist, theta, thetatest, emu) - acqtr = compute_eivar_full(theta, theta, thetatest, emu) - - ft = 20 - # Figure 3 (a)/ 2nd row - cand_theta = clist[np.argmin(acq)] - fig, ax = plt.subplots() - ax.plot(clist, acq, color="blue") - ax.set_xlabel(r"$\theta$", fontsize=ft) - ax.set_ylabel("EIVAR", fontsize=16) - ax.tick_params(axis="both", labelsize=ft) - plt.scatter(cand_theta, np.min(acq), color="green", marker="*", s=200) - plt.scatter(theta, acqtr, color="red", marker="o", s=60) - plt.savefig("Figure3_1_" + str(i) + ".png", bbox_inches="tight") - # plt.show() - - fig, ax = plt.subplots() - ax.plot(thetatest, ptest, color="black") - ax.plot(thetatest, posttesthat, color="blue", linestyle="dashed", linewidth=2.5) - plt.fill_between( - thetatest.flatten(), - (posttesthat - np.sqrt(posttestvar)).flatten(), - (posttesthat + np.sqrt(posttestvar)).flatten(), - alpha=0.3, - ) - - postcand, postcandvar = post.predict(cand_theta.reshape(1, 1)) - plt.scatter(cand_theta, postcand, color="green", marker="*", s=200) - ax.scatter(theta.T, posttrhat, color="red", s=60) - ax.set_xlabel(r"$\theta$", fontsize=ft) - ax.set_ylabel(r"$p(y|\theta)$", fontsize=ft) - ax.tick_params(axis="both", labelsize=ft) - plt.savefig("Figure3_2_" + str(i) + ".png", bbox_inches="tight") - # plt.show() - - theta = np.concatenate((theta, cand_theta.reshape(1, 1))) - f = np.concatenate((f, cls_sinlin.function(cand_theta.reshape(1, 1)))) diff --git a/examples/Technometrics2024/figure6.py b/examples/Technometrics2024/figure6.py deleted file mode 100644 index 19106d1..0000000 --- a/examples/Technometrics2024/figure6.py +++ /dev/null @@ -1,107 +0,0 @@ -import pandas as pd -import numpy as np -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments -from PUQ.prior import prior_dist -from plots import plotline -from test_funcs import unimodal, banana, bimodal, unidentifiable, create_test_data -import time - -if __name__ == "__main__": - design_start = time.time() - args = parse_arguments() - print("Running function: " + args.funcname) - - # Choose the test function - if args.funcname == "unimodal": - cls_func = unimodal() - elif args.funcname == "banana": - cls_func = banana() - elif args.funcname == "bimodal": - cls_func = bimodal() - elif args.funcname == "unidentifiable": - cls_func = unidentifiable() - - # Create a mesh for test set - xpl = np.linspace(cls_func.thetalimits[0][0], cls_func.thetalimits[0][1], 50) - ypl = np.linspace(cls_func.thetalimits[1][0], cls_func.thetalimits[1][1], 50) - Xpl, Ypl = np.meshgrid(xpl, ypl) - th = np.vstack([Xpl.ravel(), Ypl.ravel()]) - setattr(cls_func, "theta", th.T) - al_test = designer( - data_cls=cls_func, - method="SEQUNIFORM", - args={ - "mini_batch": 4, - "n_init_thetas": 10, - "nworkers": 5, - "max_evals": th.shape[1], - }, - ) - - test_data = create_test_data(al_test, cls_func) - - # Set a uniform prior - prior_func = prior_dist(dist="uniform")( - a=cls_func.thetalimits[:, 0], b=cls_func.thetalimits[:, 1] - ) - - # Define acquisition functions - acq_funcs = ["eivar", "rnd", "maxvar", "maxexp"] - datalist = [] - rep_no = 50 - n0 = 10 - # Run over 50 replications - for seed_id in range(1, rep_no + 1): - for func in acq_funcs: - print("Running " + func + " with seed " + str(seed_id)) - al_unimodal = designer( - data_cls=cls_func, - method="SEQCAL", - args={ - "mini_batch": 1, - "n_init_thetas": n0, - "nworkers": 2, - "AL": func, - "seed_n0": seed_id, - "prior": prior_func, - "data_test": test_data, - "max_evals": 210, - "type_init": None, - }, - ) - - TV = al_unimodal._info["TV"] - - for ms_id, ms in enumerate(TV): - if ms_id >= n0: - d = { - "TV": ms, - "TV_id": ms_id, - "rep": seed_id, - "batch": 1, - "methods": func, - "worker": 2, - "synth": args.funcname, - "theta_id": ms_id - n0, - } - - datalist.append(d) - - datalist.append(d) - df = pd.DataFrame(datalist) - - design_end = time.time() - print("Elapsed time: " + str(round(design_end - design_start, 2))) - - # Create the plot and save it - plotline( - df, - acq_funcs, - rep_no, - w=2, - b=1, - s=args.funcname, - ylim=[0.000005, 0.00001], - idstart=0, - ) diff --git a/examples/Technometrics2024/plots.py b/examples/Technometrics2024/plots.py deleted file mode 100644 index e81120e..0000000 --- a/examples/Technometrics2024/plots.py +++ /dev/null @@ -1,50 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - - -def plotline(df, methods, rep_no, w=2, b=1, s="banana", ylim=[0.000001, 1], idstart=0): - colors = ["red", "green", "blue", "cyan", "magenta"] - markers = ["o", "+", "*", "D", "v"] - linestyles = [(0, ()), (0, (1, 1)), (0, (5, 1)), (0, (3, 5, 1, 5)), (0, (5, 10))] - fig, ax = plt.subplots() - - idmse = 210 - maxval = 0 - minval = 1 - ft = 20 - for m_id, m in enumerate(methods): - dfnnew = df[df["methods"] == m] - dfnnnew = dfnnew[dfnnew["theta_id"] <= idmse] - - mslist = [] - sdlist = [] - for i in range(idmse + 1): - mn = dfnnnew[dfnnnew["theta_id"] == i]["TV"].mean() - sd = dfnnnew[dfnnnew["theta_id"] == i]["TV"].std() - mslist.append(mn) - sdlist.append(1.96 * sd / np.sqrt(rep_no)) - - ax.plot( - range(0, len(mslist)), - mslist, - marker=markers[m_id], - color=colors[m_id], - ls=linestyles[m_id], - markersize=10, - markevery=40, - linewidth=2, - label=m, - ) - - maxval = max(maxval, max(mslist)) - minval = min(minval, min(mslist)) - - ax.set_ylim((minval, maxval)) - ax.legend( - loc="lower center", bbox_to_anchor=(0.5, -0.55), ncol=2, prop={"size": 16} - ) # 16for small - ax.set_yscale("log") - ax.set_xlabel("Stage", fontsize=ft) - ax.set_ylabel("MAD", fontsize=ft) - ax.tick_params(axis="both", labelsize=ft) - plt.savefig("Figure6_" + s + ".png", bbox_inches="tight") diff --git a/examples/fresco_example/Figure_fresco.png b/examples/fresco_example/Figure_fresco.png index c3b7066..0c9956f 100644 Binary files a/examples/fresco_example/Figure_fresco.png and b/examples/fresco_example/Figure_fresco.png differ diff --git a/examples/fresco_example/README.md b/examples/fresco_example/README.md deleted file mode 100644 index 6b0e83b..0000000 --- a/examples/fresco_example/README.md +++ /dev/null @@ -1,27 +0,0 @@ -## Sequential Design for the Calibration of 48Ca(n,n)48Ca Reaction - -We demonstrate our sequential strategy using a nuclear physics model to predict differential cross sections as a function of angle. The angular cross sections vary with different parametrizations of the optical potential, which are inputs to the reaction code ``frescox``. ``frescox`` generates cross sections for angles ranging from 0° to 180°. This case study focuses on elastic scattering data from the 48Ca(n,n)48Ca reaction to find the optimal parametrization of the optical potential. We illustrate how ``PUQ`` and ``frescox`` work together through an example. - -Further details on this problem, including a benchmark comparing various acquisition functions and parallel implementations, can be found in Section 8.3 of the paper by [Sürer, Plumlee, and Wild, 2024](https://www.tandfonline.com/doi/abs/10.1080/00401706.2023.2246157?src=&journalCode=utch20). - -After installing ``PUQ``, ``frescox`` must also be installed to collect data using the sequential procedure. Additional notes on obtaining and building ``frescox`` can be found in the [Bfrescox README](/software/Bfrescox/README.md). - -From the root directory of ``PUQ``, navigate to the ``examples/fresco_example`` directory: -``` -cd examples/fresco_example -``` - -The template input file ``48Ca_template.in`` is provided to run the example. - -In this illustration, we acquire 64 data points (``-max_eval 64``), including an initial design of size 32 (``-n_init_thetas 32``) from a uniform prior. To acquire data points for calibrating the model via the expected integrated variance criterion (``al_func "eivar"``), run the following command: -``` -python3 puq_bfresco_test.py -max_eval 64 -al_func "eivar" -n_init_thetas 32 -``` - -Running this script takes approximately 150 seconds on a personal Mac laptop. Upon completion, the file ``Figure_fresco.jpg`` will be saved in the ``examples/fresco_example`` directory. - -After collecting data, we observe the cross sections evaluated with the acquired parameters. - -![Illustration of PUQ with Bfrescox](https://github.com/parallelUQ/PUQ/blob/main/examples/fresco_example/Figure_fresco.png) - - diff --git a/examples/fresco_example/README.rst b/examples/fresco_example/README.rst new file mode 100644 index 0000000..6bcf768 --- /dev/null +++ b/examples/fresco_example/README.rst @@ -0,0 +1,45 @@ +Sequential Design for the Calibration of 48Ca(n,n)48Ca Reaction +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We demonstrate our sequential strategy using a nuclear physics model to predict +differential cross sections as a function of angle. The angular cross sections +vary with different parametrizations of the optical potential, which are inputs +to the reaction code ``frescox``. ``frescox`` generates cross sections for angles +ranging from 0° to 180°. This case study focuses on elastic scattering data from +the 48Ca(n,n)48Ca reaction to find the optimal parametrization of the optical potential. +We illustrate how ``PUQ`` and ``frescox`` work together through an example. + +Further details on this problem, including a benchmark comparing various acquisition +functions and parallel implementations, can be found in Section 8.3 of the paper +by [Sürer, Plumlee, and Wild, 2024](https://www.tandfonline.com/doi/abs/10.1080/00401706.2023.2246157?src=&journalCode=utch20). + +After installing ``PUQ``, ``frescox`` must also be installed to collect data using +the sequential procedure. Additional notes on obtaining and building ``frescox`` +can be found in the [Bfrescox README](/software/Bfrescox/README.md). + +From the root directory of ``PUQ``, navigate to the ``examples/fresco_example`` directory: + +.. code-block:: python + + cd examples/fresco_example + + +The template input file ``48Ca_template.in`` is provided to run the example. + +In this illustration, we acquire 64 data points, including an initial design of size 32 from a uniform prior. +To acquire data points for calibrating the model via the expected integrated variance criterion, run the following command: + +.. code-block:: python + + python puq_bfresco.py + + +Running this script takes approximately 150 seconds on a personal Mac laptop. +Upon completion, the file ``Figure_fresco.png`` will be saved in the ``examples/fresco_example`` directory. + +After collecting data, we observe the cross sections evaluated with the acquired parameters. + +.. image:: Figure_fresco.png + :alt: Illustration of PUQ with Bfrescox + :align: center + :width: 600 diff --git a/examples/fresco_example/generate_test_data.py b/examples/fresco_example/generate_test_data.py index 4fef29e..a4bf2f1 100644 --- a/examples/fresco_example/generate_test_data.py +++ b/examples/fresco_example/generate_test_data.py @@ -25,11 +25,12 @@ def compute_likelihood(emumean, emuvar, obs, obsvar, is_cov): def generate_test_data(cls_synth_init): - from smt.sampling_methods import LHS + # from smt.sampling_methods import LHS + from scipy.stats import qmc data_name = cls_synth_init.data_name print("Running ", data_name) - function = cls_synth_init.function + sim = cls_synth_init.sim sh = cls_synth_init.d thetalimits = cls_synth_init.thetalimits obsvar = cls_synth_init.obsvar @@ -42,8 +43,16 @@ def generate_test_data(cls_synth_init): [thetalimits[2, :][0], thetalimits[2, :][1]], ] ) - sampling = LHS(xlimits=xlimits, random_state=1) - x = sampling(n) + # Initial sample + ndim = xlimits.shape[0] + sampler = qmc.LatinHypercube(d=ndim, seed=1) + # Generate samples in [0,1]^d + unit_sample = sampler.random(n=n) + # Scale using limits + x = qmc.scale(unit_sample, xlimits[:, 0], xlimits[:, 1]) + + # sampling = LHS(xlimits=xlimits, random_state=1) + # x = sampling(n) ftest = np.zeros((sh, n)) ptest = np.zeros(n) thetatest = np.zeros((n, cls_synth_init.p)) @@ -54,7 +63,7 @@ def generate_test_data(cls_synth_init): for i in range(n): parameter = [x[i, 0], x[i, 1], 0.6798, x[i, 2], 1.0941, 0.2763] cls_synth_init.generate_input_file(parameter) - f = function() + f = sim() ftest[:, i] = f ptest[i] = compute_likelihood( f.reshape((real_d, 1)), diff --git a/examples/fresco_example/puq_bfresco_test.py b/examples/fresco_example/puq_bfresco.py similarity index 81% rename from examples/fresco_example/puq_bfresco_test.py rename to examples/fresco_example/puq_bfresco.py index 5c75262..dffe46f 100644 --- a/examples/fresco_example/puq_bfresco_test.py +++ b/examples/fresco_example/puq_bfresco.py @@ -3,9 +3,9 @@ import numpy as np import matplotlib.pyplot as plt from PUQ.prior import prior_dist -from PUQ.design import designer -from PUQ.designmethods.utils import parse_arguments import time +from PUQ.designmethods.sequential_md_deterministic import sequential_design +from scipy.stats import qmc if __name__ == "__main__": @@ -54,6 +54,10 @@ def __init__(self): self.obsvar = np.diag(np.repeat(0.1, 15)) self.out = [("f", float, (self.d,))] + #### + self.dx = 1 + self.dt = 3 + def generate_input_file(self, parameter_values): file = "48Ca_template.in" @@ -74,7 +78,7 @@ def generate_input_file(self, parameter_values): f.writelines(content) f.close() - def function(self): + def sim(self): output_file = "48Ca_temp.out" input_file = "frescox_temp_input.in" os.system("frescox < frescox_temp_input.in > 48Ca_temp.out") @@ -99,16 +103,16 @@ def function(self): ] return f - def sim(self, H, persis_info, sim_specs, libE_info): + def function(self, theta1, theta2, theta3): """ Wraps frescox function """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) + # function = sim_specs["user"]["function"] + # H_o = np.zeros(1, dtype=sim_specs["out"]) - V = H["thetas"][0][0] - r = H["thetas"][0][1] - Ws = H["thetas"][0][2] + V = theta1 # H["thetas"][0][0] + r = theta2 # H["thetas"][0][1] + Ws = theta3 # H["thetas"][0][2] # V = 49.2849 # r = 0.9070 @@ -119,15 +123,14 @@ def sim(self, H, persis_info, sim_specs, libE_info): parameter = [V, r, a, Ws, rs, a2] self.generate_input_file(parameter) - H_o["f"] = function() + f = self.sim() for fname in os.listdir(): if fname.startswith("fort"): os.remove(fname) - return H_o, persis_info + return f design_start = time.time() - args = parse_arguments() cls_fresco = bfrescox() print("Generating test data") test_data = generate_test_data(cls_fresco) @@ -138,25 +141,41 @@ def sim(self, H, persis_info, sim_specs, libE_info): a=cls_fresco.thetalimits[:, 0], b=cls_fresco.thetalimits[:, 1] ) + # Initial sample + ndim = cls_fresco.thetalimits.shape[0] + sampler = qmc.LatinHypercube(d=ndim, seed=1) + # Generate samples in [0,1]^d + unit_sample = sampler.random(n=32) + # Scale using limits + t0 = qmc.scale( + unit_sample, cls_fresco.thetalimits[:, 0], cls_fresco.thetalimits[:, 1] + ) + f0 = np.zeros((t0.shape[0], cls_fresco.d)) + for i in range(t0.shape[0]): + f0[i, :] = cls_fresco.function(t0[i, 0], t0[i, 1], t0[i, 2]) + print("Beginning of sequential procedure") - al_fresco = designer( - data_cls=cls_fresco, - method="SEQCAL", + des_obj = sequential_design(cls_fresco) + des_obj.build_design( + z0=t0, + f0=f0, + T=64, + test=test_data, + af="ivar", args={ "mini_batch": 1, - "n_init_thetas": args.n_init_thetas, + "n_init_thetas": 10, "nworkers": 2, - "AL": args.al_func, - "seed_n0": args.seed_n0, "prior": prior_func, "data_test": test_data, - "max_evals": args.max_eval, - "type_init": None, + "seed": 1, + "integral": "LHS", }, ) print("End of sequential procedure") - theta_al = al_fresco._info["theta"] + # theta_al = al_fresco._info["theta"] + theta_al = des_obj.zs real_x = np.array( [[26, 31, 41, 51, 61, 71, 76, 81, 91, 101, 111, 121, 131, 141, 151]] @@ -184,7 +203,7 @@ def sim(self, H, persis_info, sim_specs, libE_info): dtype="float64", ) n0 = 32 - f = al_fresco._info["f"] + f = des_obj.fs # al_fresco._info["f"] fig = plt.figure(figsize=(6, 4)) ax = fig.add_subplot(1, 1, 1) @@ -203,7 +222,7 @@ def sim(self, H, persis_info, sim_specs, libE_info): label="Initial sample", ) else: - if i < 63: + if i < 95: ax.plot( np.arange(15), np.exp(f[i, :]), color="red", alpha=0.3, zorder=1 ) @@ -225,7 +244,7 @@ def sim(self, H, persis_info, sim_specs, libE_info): ax.set_ylabel("Cross section", fontsize=16) ax.set_yscale("log") ax.legend(loc="upper center", bbox_to_anchor=(0.5, -0.2), ncol=3, fontsize=10) - plt.savefig("Figure_fresco.jpg", format="jpeg", bbox_inches="tight", dpi=500) + plt.savefig("Figure_fresco.png", format="jpeg", bbox_inches="tight", dpi=500) plt.show() design_end = time.time() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..69363a4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = [ + "setuptools>=64", + "wheel", + "numpy>=1.25" # required during build +] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index d2cabb9..26dff51 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,10 @@ -numpy<2.0.0 -scipy -Cython>=3.0.10 wheel sphinx>=7.2 sphinx-rtd-theme==1.3.0 docutils<0.19 -dill \ No newline at end of file +dill +emcee +scipy +matplotlib +pandas +scikit-learn diff --git a/setup.py b/setup.py index 95a64e2..f6eef09 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,12 @@ import setuptools -from setuptools import setup, Extension +from setuptools import setup import numpy setup( name="PUQ", - version="0.1.0", - author="Özge Sürer, Matthew Plumlee, Stefan M. Wild", + version="0.1.1", + author="Özge Sürer, David O'Gara, Matthew Plumlee, Stefan M. Wild", author_email="surero@miamioh.edu", description="Python package for generating experimental designs tailored for uncertainty quantification, featuring parallel implementations", url="https://github.com/parallelUQ/PUQ", @@ -16,20 +16,6 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires=">=3.6", - install_requires=[ - "pandas", - "matplotlib", - "libensemble==1.4.2", - "torch", - "scikit-learn", - "smt", - ], - ext_modules=[ - Extension( - "PUQ.surrogatesupport.matern_covmat", - sources=["PUQ/surrogatesupport/matern_covmat.pyx"], - ), - ], + python_requires=">=3.9", include_dirs=[numpy.get_include()], ) diff --git a/tests/unit_tests/test_1d_deter.py b/tests/unit_tests/test_1d_deter.py new file mode 100644 index 0000000..50e599f --- /dev/null +++ b/tests/unit_tests/test_1d_deter.py @@ -0,0 +1,79 @@ +"""Tests whether the we obtain a design for a one-dimensional deterministic simulation model""" + +import sys + +sys.path.append("./") +import numpy as np +from scipy.stats import qmc +from PUQ.designmethods.sequential_1d_deterministic import sequential_design + + +class sinfunc: + def __init__(self): + self.data_name = "sinfunc" + self.zlim = np.array([[0, 1], [0, 1]]) + self.true_theta = np.array([np.pi / 5]) + self.out = [("f", float)] + self.d = 1 + self.p = 2 + self.dx = 1 + self.dt = 1 + self.x = None + self.real_data = None + self.sigma2 = 0.2**2 + self.nodata = True + + def function(self, x, theta): + f = np.sin(10 * x - 5 * theta) + return f + + def realdata(self, x, seed, isbias=False): + self.x = x + self.d = len(x) + self.nodata = False + self.obsvar = np.diag(np.repeat(self.sigma2, len(self.x))) + + np.random.seed(seed) + fevals = np.zeros(len(x)) + for xid, x in enumerate(self.x): + fevals[xid] = self.genobsdata(x, isbias) + + self.real_data = np.array([fevals], dtype="float64") + + def genobsdata(self, x, isbias=False): + return self.function(x[0], self.true_theta[0]) + np.random.normal( + 0, np.sqrt(self.sigma2), 1 + ) + + +cex = sinfunc() +dt = len(cex.true_theta) +x_obs = np.array([0.1, 0.1, 0.3, 0.3, 0.5, 0.5, 0.7, 0.7, 0.9, 0.9])[:, None] +cex.realdata(x=x_obs, seed=1) + + +# Generate initial sample +n0, nmax = 10, 30 +sampling = qmc.LatinHypercube(d=cex.zlim.shape[0], seed=1) +z0 = sampling.random(n=n0) +f0 = np.array([cex.function(z0[i, 0], z0[i, 1]) for i in range(n0)]) + + +def test_build_design(): + + # Generate design + des_obj = sequential_design(cex) + des_obj.build_design( + z0=z0, + f0=f0[:, None], + T=nmax, + af="ivar", + args={"nL": 200, "seed": 1, "integral": "importance"}, + ) + + assert des_obj.zs.shape == (n0 + nmax, 2) + assert des_obj.fs.shape == (n0 + nmax, 1) + + +if __name__ == "__main__": + test_build_design() diff --git a/tests/unit_tests/test_UQ_ijoc.py b/tests/unit_tests/test_UQ_ijoc.py deleted file mode 100644 index bd25fb9..0000000 --- a/tests/unit_tests/test_UQ_ijoc.py +++ /dev/null @@ -1,111 +0,0 @@ -import numpy as np -import pytest -from contextlib import contextmanager -from PUQ.design import designer -from PUQ.prior import prior_dist -import scipy.stats as sps - - -class himmelblau: - def __init__(self): - - self.data_name = "himmelblau" - self.thetalimits = np.array([[0, 1], [0, 1]]) - self.truelimits = np.array([[-5, 5], [-5, 5]]) - self.obsvar = np.array([[1]], dtype="float64") - self.real_data = np.array([[1]], dtype="float64") - self.out = [("f", float)] - self.p = 2 - self.d = 1 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - - theta1 = self.truelimits[0][0] + theta1 * ( - self.truelimits[0][1] - self.truelimits[0][0] - ) - theta2 = self.truelimits[1][0] + theta2 * ( - self.truelimits[1][1] - self.truelimits[1][0] - ) - f = (theta1**2 + theta2 - 11) ** 2 + (theta1 + theta2**2 - 7) ** 2 - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the himmelblau function - """ - - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - return H_o, persis_info - - -example = "himmelblau" -cls_data = eval(example)() - -# # # Create a mesh for test set # # # -xpl = np.linspace(cls_data.thetalimits[0][0], cls_data.thetalimits[0][1], 50) -ypl = np.linspace(cls_data.thetalimits[1][0], cls_data.thetalimits[1][1], 50) -Xpl, Ypl = np.meshgrid(xpl, ypl) -th = np.vstack([Xpl.ravel(), Ypl.ravel()]) -setattr(cls_data, "theta", th.T) - -ftest = np.zeros(2500) -for tid, t in enumerate(th.T): - ftest[tid] = cls_data.function(t[0], t[1]) -thetatest = th.T -ptest = np.zeros(thetatest.shape[0]) -for i in range(ftest.shape[0]): - mean = ftest[i] - rnd = sps.multivariate_normal(mean=mean, cov=cls_data.obsvar) - ptest[i] = rnd.pdf(cls_data.real_data) - -test_data = {"theta": thetatest, "f": ftest, "p": ptest, "p_prior": 1} - -# # # # # # # # # # # # # # # # # # # # # -prior_func = prior_dist(dist="uniform")( - a=cls_data.thetalimits[:, 0], b=cls_data.thetalimits[:, 1] -) - -n_init = 10 -s = 0 -thetainit = prior_func.rnd(n_init, s) -finit = np.zeros(n_init) -for tid, t in enumerate(thetainit): - finit[tid] = cls_data.function(t[0], t[1]) -test_data["thetainit"] = thetainit -test_data["finit"] = finit[None, :] - - -@contextmanager -def does_not_raise(): - yield - - -@pytest.mark.parametrize( - "input1,expectation", - [("ei", does_not_raise()), ("hybrid_ei", does_not_raise())], -) -def test_none_input(input1, expectation): - with expectation: - assert ( - designer( - data_cls=cls_data, - method="SEQCALOPT", - args={ - "mini_batch": 1, - "nworkers": 2, - "AL": input1, - "seed_n0": 0, - "prior": prior_func, - "data_test": test_data, - "max_evals": 50, - "candsize": 100, - "refsize": 100, - "believer": 0, - }, - ) - is not None - ) diff --git a/tests/unit_tests/test_UQ_techno.py b/tests/unit_tests/test_UQ_techno.py deleted file mode 100644 index 24a22de..0000000 --- a/tests/unit_tests/test_UQ_techno.py +++ /dev/null @@ -1,78 +0,0 @@ -import numpy as np -import pytest -from contextlib import contextmanager -from PUQ.design import designer -from PUQ.prior import prior_dist - - -class unimodal: - def __init__(self): - self.data_name = "unimodal" - self.thetalimits = np.array([[-4, 4], [-4, 4]]) - self.obsvar = np.array([[4]], dtype="float64") - self.real_data = np.array([[-6]], dtype="float64") - self.out = [("f", float)] - self.d = 1 - self.p = 2 - self.x = np.arange(0, self.d)[:, None] - self.real_x = np.arange(0, self.d)[:, None] - - def function(self, theta1, theta2): - """ - Wraps the unimodal function - """ - thetas = np.array([theta1, theta2]).reshape((1, 2)) - S = np.array([[1, 0.5], [0.5, 1]]) - f = (thetas @ S) @ thetas.T - return f - - def sim(self, H, persis_info, sim_specs, libE_info): - """ - Wraps the simulator - """ - function = sim_specs["user"]["function"] - H_o = np.zeros(1, dtype=sim_specs["out"]) - H_o["f"] = function(H["thetas"][0][0], H["thetas"][0][1]) - - return H_o, persis_info - - -cls_unimodal = unimodal() -prior_func = prior_dist(dist="uniform")( - a=cls_unimodal.thetalimits[:, 0], b=cls_unimodal.thetalimits[:, 1] -) - - -@contextmanager -def does_not_raise(): - yield - - -@pytest.mark.parametrize( - "input1,expectation", - [ - ("maxvar", does_not_raise()), - ("maxexp", does_not_raise()), - ("rnd", does_not_raise()), - ], -) -def test_none_input(input1, expectation): - with expectation: - assert ( - designer( - data_cls=cls_unimodal, - method="SEQCAL", - args={ - "mini_batch": 1, - "n_init_thetas": 10, - "nworkers": 2, - "AL": input1, - "seed_n0": 1, - "prior": prior_func, - "data_test": None, - "max_evals": 60, - "type_init": None, - }, - ) - is not None - ) diff --git a/tests/unit_tests/test_hetGP.py b/tests/unit_tests/test_hetGP.py new file mode 100644 index 0000000..427e714 --- /dev/null +++ b/tests/unit_tests/test_hetGP.py @@ -0,0 +1,154 @@ +"""Tests whether the emulator with hetGP gives the same result as just using hetGP""" + +import sys + +sys.path.append("./") +import numpy as np +from hetgpy import hetGP +from PUQ.surrogate import emulator + +# generate some test data +rng = np.random.default_rng(123) + +X = np.linspace(0, 2 * np.pi, 20).reshape(-1, 1) +# add some replicates +reps = rng.choice(len(X), size=100, replace=True) +X = X[reps,] +Ytrue = np.sin(X) +noise = 0.5 * rng.normal(size=(len(Ytrue), 1)) + +Y = Ytrue + noise + +# prediction grid (interpolation) +Xgrid = np.linspace(X.min(), X.max(), 100).reshape(-1, 1) + + +def test_fit(): + reference_model = hetGP() + reference_model.mle(X, Y.flatten()) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="hetGP") + test_model.fit() + + assert test_model._info["ll"] == reference_model["ll"] + assert test_model._info["theta"] == reference_model["theta"] + assert test_model._info["g"] == reference_model["g"] + assert test_model._info["beta0"] == reference_model["beta0"] + assert np.allclose(test_model._info["Delta"], reference_model["Delta"]) + assert np.allclose(test_model._info["Lambda"], reference_model["Lambda"]) + + +def test_matern(): + # test a different covariance type and define some input args + COVTYPE = "Matern5_2" + MAXIT = 50 + SETTINGS = {"return.Ki": False, "factr": 1e5} + + reference_model = hetGP() + reference_model.mle(X, Y.flatten(), covtype=COVTYPE, maxit=MAXIT, settings=SETTINGS) + + test_model = emulator( + x=X, + theta=np.array([0]), + f=Y, + method="hetGP", + args={"covtype": COVTYPE, "maxit": MAXIT, "settings": SETTINGS}, + ) + test_model.fit() + + assert test_model._info["ll"] == reference_model["ll"] + assert test_model._info["theta"] == reference_model["theta"] + assert test_model._info["g"] == reference_model["g"] + assert test_model._info["beta0"] == reference_model["beta0"] + assert np.allclose(test_model._info["Delta"], reference_model["Delta"]) + assert np.allclose(test_model._info["Lambda"], reference_model["Lambda"]) + + +def test_predict(): + # refits models and then tests predictions + + reference_model = hetGP() + reference_model.mle(X, Y.flatten()) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="hetGP") + test_model.fit() + + # test predictions + preds = reference_model.predict(Xgrid, xprime=Xgrid) + + test_preds = test_model.predict(x=Xgrid, thetaprime=Xgrid) + assert np.allclose(test_preds._info["mean"], preds["mean"]) + assert np.allclose(test_preds._info["var"], preds["sd2"]) + assert np.allclose(test_preds._info["nugs"], preds["nugs"]) + assert np.allclose(test_preds._info["covmat"], preds["cov"]) + + +def test_predict_nugs_only(): + + reference_model = hetGP() + reference_model.mle(X, Y.flatten()) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="hetGP") + test_model.fit() + + # test predictions + preds = reference_model.predict(Xgrid, xprime=Xgrid, nugs_only=True) + + test_preds = test_model.predict( + x=Xgrid, thetaprime=Xgrid, args=dict(nugs_only=True) + ) + for key in ["mean", "var", "covmat"]: + assert test_preds._info.get(key) is None + assert np.allclose(test_preds._info["nugs"], preds["nugs"]) + + +def test_hetGP_update_kriging_believer(): + reference_model = hetGP() + reference_model.mle(X, Y.flatten()) + + Xnew = X.mean().reshape(-1, 1) + Ypred = reference_model.predict(Xnew)["mean"] + reference_model.update(Xnew, Ypred, maxit=0) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="hetGP") + test_model.fit() + test_model.update(Xnew) + + assert test_model._info["ll"] == reference_model["ll"] + assert test_model._info["theta"] == reference_model["theta"] + assert test_model._info["g"] == reference_model["g"] + assert test_model._info["beta0"] == reference_model["beta0"] + + +def test_hetGP_update(): + reference_model = hetGP() + reference_model.mle(X, Y.flatten()) + + Xnew = X.mean().reshape(-1, 1) + Ypred = reference_model.predict(Xnew)["mean"] + reference_model.update(Xnew, Ypred) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="hetGP") + test_model.fit() + test_model.update(x=Xnew, Y=Ypred) + + assert test_model._info["ll"] == reference_model["ll"] + assert test_model._info["theta"] == reference_model["theta"] + assert test_model._info["g"] == reference_model["g"] + assert test_model._info["beta0"] == reference_model["beta0"] + + +def test_conversion_to_homGP(): + # should return homoskedastic GP + X = np.linspace(0, 1, 20).reshape(-1, 1) + Y = X + model = emulator(x=X, theta=np.array([0]), f=Y, method="hetGP") + Xp = np.linspace(X.min(), X.max(), 100).reshape(-1, 1) + preds = model.predict(Xp) + assert model._info["is_homGP"] + assert model._info.get("Delta") is None + assert np.unique(preds._info["nugs"]).shape[0] == 1 + + +if __name__ == "__main__": + test_conversion_to_homGP() diff --git a/tests/unit_tests/test_homGP.py b/tests/unit_tests/test_homGP.py new file mode 100644 index 0000000..f5ce2d1 --- /dev/null +++ b/tests/unit_tests/test_homGP.py @@ -0,0 +1,139 @@ +"""Tests whether the emulator with homGP gives the same result as just using homGP""" + +import sys + +sys.path.append("./") +import numpy as np +from hetgpy import homGP +from PUQ.surrogate import emulator + +# generate some test data +rng = np.random.default_rng(123) + +X = np.linspace(0, 2 * np.pi, 20).reshape(-1, 1) +# add some replicates +reps = rng.choice(len(X), size=100, replace=True) +X = X[reps,] +Ytrue = np.sin(X) +noise = 0.5 * rng.normal(size=(len(Ytrue), 1)) + +Y = Ytrue + noise + +# prediction grid (interpolation) +Xgrid = np.linspace(X.min(), X.max(), 100).reshape(-1, 1) + + +def test_fit(): + + reference_model = homGP() + reference_model.mle(X, Y.flatten()) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="homGP") + test_model.fit() + + assert test_model._info["ll"] == reference_model["ll"] + assert test_model._info["theta"] == reference_model["theta"] + assert test_model._info["g"] == reference_model["g"] + assert test_model._info["beta0"] == reference_model["beta0"] + + +def test_predict(): + # refits models and then tests predictions + + reference_model = homGP() + reference_model.mle(X, Y.flatten()) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="homGP") + test_model.fit() + + # test predictions + preds = reference_model.predict(Xgrid, xprime=Xgrid) + + test_preds = test_model.predict(x=Xgrid, thetaprime=Xgrid) + assert np.allclose(test_preds._info["mean"], preds["mean"]) + assert np.allclose(test_preds._info["var"], preds["sd2"]) + assert np.allclose(test_preds._info["nugs"], preds["nugs"]) + assert np.allclose(test_preds._info["covmat"], preds["cov"]) + + +def test_predict_nugs_only(): + + reference_model = homGP() + reference_model.mle(X, Y.flatten()) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="homGP") + test_model.fit() + + # test predictions + preds = reference_model.predict(Xgrid, xprime=Xgrid, nugs_only=True) + + test_preds = test_model.predict( + x=Xgrid, thetaprime=Xgrid, args=dict(nugs_only=True) + ) + for key in ["mean", "var", "covmat"]: + assert test_preds._info.get(key) is None + assert np.allclose(test_preds._info["nugs"], preds["nugs"]) + + +def test_matern(): + # test a different covariance type and define some input args + COVTYPE = "Matern5_2" + MAXIT = 50 + SETTINGS = {"return.Ki": False, "factr": 1e5} + + reference_model = homGP() + reference_model.mle(X, Y.flatten(), covtype=COVTYPE, maxit=MAXIT, settings=SETTINGS) + + test_model = emulator( + x=X, + theta=np.array([0]), + f=Y, + method="homGP", + args={"covtype": COVTYPE, "maxit": MAXIT, "settings": SETTINGS}, + ) + test_model.fit() + + assert test_model._info["ll"] == reference_model["ll"] + assert test_model._info["theta"] == reference_model["theta"] + assert test_model._info["g"] == reference_model["g"] + assert test_model._info["beta0"] == reference_model["beta0"] + + +def test_homGP_update_kriging_believer(): + reference_model = homGP() + reference_model.mle(X, Y.flatten()) + + Xnew = X.mean().reshape(-1, 1) + Ypred = reference_model.predict(Xnew)["mean"] + reference_model.update(Xnew, Ypred, maxit=0) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="homGP") + test_model.fit() + test_model.update(Xnew) + + assert test_model._info["ll"] == reference_model["ll"] + assert test_model._info["theta"] == reference_model["theta"] + assert test_model._info["g"] == reference_model["g"] + assert test_model._info["beta0"] == reference_model["beta0"] + + +def test_homGP_update(): + reference_model = homGP() + reference_model.mle(X, Y.flatten()) + + Xnew = X.mean().reshape(-1, 1) + Ypred = reference_model.predict(Xnew)["mean"] + reference_model.update(Xnew, Ypred) + + test_model = emulator(x=X, theta=np.array([0]), f=Y, method="homGP") + test_model.fit() + test_model.update(x=Xnew, Y=Ypred) + + assert test_model._info["ll"] == reference_model["ll"] + assert test_model._info["theta"] == reference_model["theta"] + assert test_model._info["g"] == reference_model["g"] + assert test_model._info["beta0"] == reference_model["beta0"] + + +if __name__ == "__main__": + test_homGP_update() diff --git a/tests/unit_tests/test_multihetGP.py b/tests/unit_tests/test_multihetGP.py new file mode 100644 index 0000000..15ee070 --- /dev/null +++ b/tests/unit_tests/test_multihetGP.py @@ -0,0 +1,185 @@ +import sys + +sys.path.append("./") +import numpy as np +from scipy.stats import qmc +from PUQ.surrogate import emulator +from hetgpy import hetGP +from copy import deepcopy + +# create some noise 2D data +rng = np.random.default_rng(1) +lhs = qmc.LatinHypercube(d=2, rng=rng) + +X = lhs.random(n=100) +reps = rng.choice(len(X), size=100, replace=True) +X = X[reps,] +# prediction grid +Xp = lhs.random(n=100) +Y = 0 * X +Y[:, 0] = np.sin(X[:, 0]) +Y[:, 1] = np.cos(X[:, 1]) +# varying noise field +noise = rng.normal(size=Y.shape) * np.exp(-(X**2)) +noise *= 2 + +Y += noise + +COVTYPE = "Matern5_2" + +# module wide settings +SETTINGS = { + "linkThetas": "joint", + "logN": True, + "initStrategy": "residuals", + "checkHom": True, + "penalty": True, + "trace": 0, + "return.matrices": True, + "return.hom": False, + "factr": 1e9, +} +NOISECONTROL = {"k_theta_g_bounds": (1, 100), "g_max": 1e2, "g_bounds": (1e-6, 1)} + +MAXIT = 100 + + +def test_fit(): + GPlist = [ + hetGP().mle( + X=X, + Z=Y[:, i], + covtype=COVTYPE, + maxit=MAXIT, + settings=SETTINGS, + noiseControl=NOISECONTROL, + ) + for i in range(Y.shape[1]) + ] + multiGP = emulator( + x=X, + theta=np.array([0, 1]).reshape(2, 1), + f=Y, + method="multihetGP", + args={ + "covtype": COVTYPE, + "maxit": MAXIT, + "settings": SETTINGS, + "noiseControl": NOISECONTROL, + }, + ) + # multiGP.fit() + + emulator_keys = ["ll", "theta", "beta0", "Delta"] + for i in range(len(GPlist)): + GP = GPlist[i] + for key in emulator_keys: + + assert np.allclose(multiGP._info["emulist"][i]._info[key], GP[key]) + + return + + +def test_predict(): + + multiGP = emulator( + x=X, + theta=np.array([0, 1]).reshape(2, 1), + f=Y, + method="multihetGP", + args={"covtype": COVTYPE, "maxit": MAXIT, "settings": SETTINGS}, + ) + multiGP.fit() + preds = multiGP.predict(x=Xp, thetaprime=Xp) + GPlist = [ + hetGP() + .mle(X=X, Z=Y[:, i], covtype=COVTYPE, maxit=MAXIT, settings=SETTINGS) + .predict(Xp, xprime=Xp) + for i in range(Y.shape[1]) + ] + em2GPkey = {"mean": "mean", "var": "sd2", "nugs": "nugs"} + for i in range(len(GPlist)): + GP = GPlist[i] + for em_key, GP_key in em2GPkey.items(): + + assert np.allclose(preds._info[em_key][i, :], GP[GP_key]) + + # test cov and covmat + for i in range(len(GPlist)): + assert np.allclose(preds._info["covmat"][i, :, :], GPlist[i]["cov"]) + + return + + +def test_update_kriging_believer(): + + multiGP = emulator( + x=X, + theta=np.array([0, 1]).reshape(2, 1), + f=Y, + method="multihetGP", + args={"covtype": COVTYPE, "maxit": MAXIT, "settings": SETTINGS}, + ) + multiGP.fit() + initial_lls = [ + multiGP._info["emulist"][i]._info["ll"] for i in range(multiGP._info["numGPs"]) + ] + initial_lls = deepcopy(initial_lls) + GPlist = [ + hetGP().mle(X=X, Z=Y[:, i], covtype=COVTYPE, maxit=MAXIT, settings=SETTINGS) + for i in range(Y.shape[1]) + ] + GPlist_initial = deepcopy(GPlist) + Xnew = X.mean(axis=0).reshape(-1, X.shape[1]) + multiGP.update(x=Xnew) + em_keys = ["ll", "theta", "beta0", "Delta"] + for GP in GPlist: + Ynew = GP.predict(Xnew)["mean"] + GP.update(Xnew, Ynew, maxit=0) + for i in range(multiGP._info["numGPs"]): + GP = GPlist[i] + for key in em_keys: + assert np.allclose(multiGP._info["emulist"][i]._info[key], GP[key]) + + +def test_update(): + """Test update and that log likelihood changes""" + + multiGP = emulator( + x=X, + theta=np.array([0, 1]).reshape(2, 1), + f=Y, + method="multihetGP", + args={"covtype": COVTYPE, "maxit": MAXIT, "settings": SETTINGS}, + ) + multiGP.fit() + initial_lls = [ + multiGP._info["emulist"][i]._info["ll"] for i in range(multiGP._info["numGPs"]) + ] + initial_lls = deepcopy(initial_lls) + + GPlist = [ + hetGP().mle(X=X, Z=Y[:, i], covtype=COVTYPE, maxit=MAXIT, settings=SETTINGS) + for i in range(Y.shape[1]) + ] + GPlist_initial = deepcopy(GPlist) + Xnew = X.mean(axis=0).reshape(-1, X.shape[1]) + mpreds = multiGP.predict(x=Xnew) + Ynew = mpreds._info["mean"].T + multiGP.update(x=Xnew, Y=Ynew) + em_keys = ["ll", "theta", "beta0", "Delta"] + for GP in GPlist: + Ynew = GP.predict(Xnew)["mean"] + GP.update(Xnew, Ynew, maxit=MAXIT) + for i in range(multiGP._info["numGPs"]): + GP = GPlist[i] + for key in em_keys: + assert np.allclose(multiGP._info["emulist"][i]._info[key], GP[key]) + # also check that likelihoods changed after update + for i in range(len(initial_lls)): + old_ll = initial_lls[i] + assert not np.allclose(old_ll, multiGP._info["emulist"][i]._info["ll"]) + + +if __name__ == "__main__": + test_update() diff --git a/tests/unit_tests/test_multihomGP.py b/tests/unit_tests/test_multihomGP.py new file mode 100644 index 0000000..2b1aab2 --- /dev/null +++ b/tests/unit_tests/test_multihomGP.py @@ -0,0 +1,153 @@ +import sys + +sys.path.append("./") +import numpy as np +from scipy.stats import qmc +from PUQ.surrogate import emulator +from hetgpy import homGP +from copy import deepcopy + +# create some noise 2D data +rng = np.random.default_rng(1) +lhs = qmc.LatinHypercube(d=2, rng=rng) + +X = lhs.random(n=50) +# prediction grid +Xp = lhs.random(n=100) +Y = 0 * X +Y[:, 0] = np.sin(X[:, 0]) +Y[:, 1] = np.cos(X[:, 1]) +noise = rng.normal(size=Y.shape) + +Y += noise + +COVTYPE = "Matern5_2" + + +def test_fit(): + SETTINGS = {"return.Ki": False, "factr": 1e9} + multiGP = emulator( + x=X, + theta=np.array([0, 1]).reshape(2, 1), + f=Y, + method="multihomGP", + args={"covtype": COVTYPE, "maxit": 50, "settings": SETTINGS}, + ) + multiGP.fit() + GPlist = [ + homGP().mle(X=X, Z=Y[:, i], covtype=COVTYPE, maxit=50, settings=SETTINGS) + for i in range(Y.shape[1]) + ] + emulator_keys = ["ll", "theta", "beta0"] + for i in range(len(GPlist)): + GP = GPlist[i] + for key in emulator_keys: + + assert np.allclose(multiGP._info["emulist"][i]._info[key], GP[key]) + + return + + +def test_predict(): + SETTINGS = {"return.Ki": False, "factr": 1e9} + multiGP = emulator( + x=X, + theta=np.array([0, 1]).reshape(2, 1), + f=Y, + method="multihomGP", + args={"covtype": COVTYPE, "maxit": 50, "settings": SETTINGS}, + ) + multiGP.fit() + preds = multiGP.predict(x=Xp, thetaprime=Xp) + GPlist = [ + homGP() + .mle(X=X, Z=Y[:, i], covtype=COVTYPE, maxit=50, settings=SETTINGS) + .predict(Xp, xprime=Xp) + for i in range(Y.shape[1]) + ] + em2GPkey = {"mean": "mean", "var": "sd2"} + for i in range(len(GPlist)): + GP = GPlist[i] + for em_key, GP_key in em2GPkey.items(): + + assert np.allclose(preds._info[em_key][i, :], GP[GP_key]) + + # test cov and covmat + for i in range(len(GPlist)): + assert np.allclose(preds._info["covmat"][i, :, :], GPlist[i]["cov"]) + + return + + +def test_update_kriging_believer(): + SETTINGS = {"return.Ki": False, "factr": 1e9} + multiGP = emulator( + x=X, + theta=np.array([0, 1]).reshape(2, 1), + f=Y, + method="multihomGP", + args={"covtype": COVTYPE, "maxit": 50, "settings": SETTINGS}, + ) + multiGP.fit() + initial_lls = [ + multiGP._info["emulist"][i]._info["ll"] for i in range(multiGP._info["numGPs"]) + ] + initial_lls = deepcopy(initial_lls) + GPlist = [ + homGP().mle(X=X, Z=Y[:, i], covtype=COVTYPE, maxit=50, settings=SETTINGS) + for i in range(Y.shape[1]) + ] + GPlist_initial = deepcopy(GPlist) + Xnew = X.mean(axis=0).reshape(-1, X.shape[1]) + multiGP.update(x=Xnew) + em_keys = ["ll", "theta", "beta0"] + for GP in GPlist: + Ynew = GP.predict(Xnew)["mean"] + GP.update(Xnew, Ynew, maxit=0) + for i in range(multiGP._info["numGPs"]): + GP = GPlist[i] + for key in em_keys: + assert np.allclose(multiGP._info["emulist"][i]._info[key], GP[key]) + + +def test_update(): + """Test update and that log likelihood changes""" + SETTINGS = {"return.Ki": False, "factr": 1e9} + multiGP = emulator( + x=X, + theta=np.array([0, 1]).reshape(2, 1), + f=Y, + method="multihomGP", + args={"covtype": COVTYPE, "maxit": 50, "settings": SETTINGS}, + ) + multiGP.fit() + initial_lls = [ + multiGP._info["emulist"][i]._info["ll"] for i in range(multiGP._info["numGPs"]) + ] + initial_lls = deepcopy(initial_lls) + + GPlist = [ + homGP().mle(X=X, Z=Y[:, i], covtype=COVTYPE, maxit=50, settings=SETTINGS) + for i in range(Y.shape[1]) + ] + GPlist_initial = deepcopy(GPlist) + Xnew = X.mean(axis=0).reshape(-1, X.shape[1]) + mpreds = multiGP.predict(x=Xnew) + Ynew = mpreds._info["mean"].T + multiGP.update(x=Xnew, Y=Ynew) + em_keys = ["ll", "theta", "beta0"] + for GP in GPlist: + Ynew = GP.predict(Xnew)["mean"] + GP.update(Xnew, Ynew, maxit=50) + for i in range(multiGP._info["numGPs"]): + GP = GPlist[i] + for key in em_keys: + assert np.allclose(multiGP._info["emulist"][i]._info[key], GP[key]) + # also check that likelihoods changed after update + for i in range(len(initial_lls)): + old_ll = initial_lls[i] + assert not np.allclose(old_ll, multiGP._info["emulist"][i]._info["ll"]) + + +if __name__ == "__main__": + test_update_kriging_believer()