From 3e6c03e59e4ebfe5d429ec51677ae339d825a177 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 15:02:11 -0500 Subject: [PATCH 01/50] trying to implement auto testing --- .github/workflows/CI_testing.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/CI_testing.yml diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml new file mode 100644 index 00000000..5f5ba2f8 --- /dev/null +++ b/.github/workflows/CI_testing.yml @@ -0,0 +1,22 @@ +name: CI +on: + push: + branches: + - main + +defaults: + run: + shell: bash -l {0} +jobs: + style: + runs-on: ubuntu-22.04 + container: + image: dolfinx/dolfinx:stable + options: --user 1001 --privileged + name: CI test + steps: + - name: run code + uses: actions/checkout@v2 + - name: run the file + run: cd example + run: mpirun -n 1 python3 -u test_file.py From 2f7b6247715c6382c6019396651e3ed27409fe9b Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 15:05:13 -0500 Subject: [PATCH 02/50] trying to implement auto testing --- .github/workflows/CI_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 5f5ba2f8..37e17d16 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -2,7 +2,7 @@ name: CI on: push: branches: - - main + - numpy_debug_v2 defaults: run: From f90e745c5c633cdf32e6a522884856d7b07bdde6 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 15:06:49 -0500 Subject: [PATCH 03/50] trying to implement auto testing --- .github/workflows/CI_testing.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 37e17d16..e32a0727 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -18,5 +18,4 @@ jobs: - name: run code uses: actions/checkout@v2 - name: run the file - run: cd example - run: mpirun -n 1 python3 -u test_file.py + run: cd example && mpirun -n 1 python3 -u test_file.py From 75e92b2d6dbe7250acbea53dfddae7bd644f9230 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 15:08:14 -0500 Subject: [PATCH 04/50] trying to implement auto testing --- .github/workflows/CI_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index e32a0727..63e90814 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -18,4 +18,4 @@ jobs: - name: run code uses: actions/checkout@v2 - name: run the file - run: cd example && mpirun -n 1 python3 -u test_file.py + run: cd example && mpirun -n 1 python3 -u poisson_example.py From 08b668a6f70e6e3f1ef674f1a737563142ab08ae Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 20:31:51 -0500 Subject: [PATCH 05/50] implement CI --- .github/workflows/CI_testing.yml | 11 ++++-- example/sfsi_toy_gaussian.py | 59 ++++++++++++++++++------------- hippylibX/modeling/modelVerify.py | 2 +- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 63e90814..e4027bc5 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -2,7 +2,7 @@ name: CI on: push: branches: - - numpy_debug_v2 + - main defaults: run: @@ -18,4 +18,11 @@ jobs: - name: run code uses: actions/checkout@v2 - name: run the file - run: cd example && mpirun -n 1 python3 -u poisson_example.py + run: cd ./example && mpirun -n 1 python3 -u sfsi_toy_gaussian.py + - name: change directory and validate + run: | + cd ./example/testing_folder && + output=$(python3 ./test_script.py) && + if [[ "$output" == "0" ]]; then + exit 1 + fi \ No newline at end of file diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index d342040a..252bdb4b 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -8,6 +8,9 @@ import os import dolfinx.fem.petsc +# for validation purposes only - to write out modelVerify results +import pickle + from matplotlib import pyplot as plt @@ -124,8 +127,16 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict hpx.parRandom.normal(1.,noise) prior.sample(noise,m0) - hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + eps, err_grad, err_H, rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + + if(rank == 0): + data = {"xvals":eps,"arr_1":err_grad, "arr_2": err_H, "sym_Hessian_value":rel_symm_error} + os.makedirs('testing_folder',exist_ok=True) + with open('testing_folder/outputs.pickle','wb') as f: + pickle.dump(data,f) + + # if(rank == 0): # print(err_grad,'\n') @@ -134,37 +145,37 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict # ####################################### - prior_mean_copy = prior.generate_parameter(0) - prior_mean_copy.array[:] = prior_mean.array[:] + # prior_mean_copy = prior.generate_parameter(0) + # prior_mean_copy.array[:] = prior_mean.array[:] - x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + # x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] - if rank == 0: - print( sep, "Find the MAP point", sep) + # if rank == 0: + # print( sep, "Find the MAP point", sep) - parameters = hpx.ReducedSpaceNewtonCG_ParameterList() - parameters["rel_tolerance"] = 1e-6 - parameters["abs_tolerance"] = 1e-9 - parameters["max_iter"] = 500 - parameters["cg_coarse_tolerance"] = 5e-1 - parameters["globalization"] = "LS" - parameters["GN_iter"] = 20 - if rank != 0: - parameters["print_level"] = -1 + # parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + # parameters["rel_tolerance"] = 1e-6 + # parameters["abs_tolerance"] = 1e-9 + # parameters["max_iter"] = 500 + # parameters["cg_coarse_tolerance"] = 5e-1 + # parameters["globalization"] = "LS" + # parameters["GN_iter"] = 20 + # if rank != 0: + # parameters["print_level"] = -1 - solver = hpx.ReducedSpaceNewtonCG(model, parameters) + # solver = hpx.ReducedSpaceNewtonCG(model, parameters) - x = solver.solve(x) + # x = solver.solve(x) - if solver.converged: - master_print(comm, "\nConverged in ", solver.it, " iterations.") - else: - master_print(comm, "\nNot Converged") + # if solver.converged: + # master_print(comm, "\nConverged in ", solver.it, " iterations.") + # else: + # master_print(comm, "\nNot Converged") - master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) - master_print (comm, "Final gradient norm: ", solver.final_grad_norm) - master_print (comm, "Final cost: ", solver.final_cost) + # master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + # master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + # master_print (comm, "Final cost: ", solver.final_cost) ####################################### diff --git a/hippylibX/modeling/modelVerify.py b/hippylibX/modeling/modelVerify.py index f4eba554..a1dd288a 100644 --- a/hippylibX/modeling/modelVerify.py +++ b/hippylibX/modeling/modelVerify.py @@ -129,7 +129,7 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr if rel_symm_error > 1e-10: print( "HESSIAN IS NOT SYMMETRIC!!") - return eps, err_grad, err_H + return eps, err_grad, err_H, rel_symm_error def modelVerifyPlotErrors(comm, misfit_only, is_quadratic : bool, eps : np.ndarray, err_grad : np.ndarray, err_H : np.ndarray) -> None: From 269dcb508467681d48f0ebce08d4bf1d98772d2d Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 20:34:30 -0500 Subject: [PATCH 06/50] implement CI --- .github/workflows/CI_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index e4027bc5..4db0e150 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -2,7 +2,7 @@ name: CI on: push: branches: - - main + - numpy_debug_v2 defaults: run: From 65bd967520bb13a3cf132760265f5defd99f2103 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 20:36:29 -0500 Subject: [PATCH 07/50] implement CI --- .github/workflows/CI_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 4db0e150..70fd5818 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -22,7 +22,7 @@ jobs: - name: change directory and validate run: | cd ./example/testing_folder && - output=$(python3 ./test_script.py) && + output=$(python3 test_script.py) && if [[ "$output" == "0" ]]; then exit 1 fi \ No newline at end of file From 3faa6eb62aab325fe9bde10af83709c8326488cb Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 20:39:48 -0500 Subject: [PATCH 08/50] implement CI --- .github/workflows/CI_testing.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 70fd5818..b39a7146 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -10,15 +10,15 @@ defaults: jobs: style: runs-on: ubuntu-22.04 - container: - image: dolfinx/dolfinx:stable - options: --user 1001 --privileged + # container: + # image: dolfinx/dolfinx:stable + # options: --user 1001 --privileged name: CI test steps: - name: run code uses: actions/checkout@v2 - - name: run the file - run: cd ./example && mpirun -n 1 python3 -u sfsi_toy_gaussian.py + # - name: run the file + # run: cd ./example && mpirun -n 1 python3 -u sfsi_toy_gaussian.py - name: change directory and validate run: | cd ./example/testing_folder && From e1228b61cedf366df98033ebd248adffdaa2af86 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 20:40:23 -0500 Subject: [PATCH 09/50] implement CI --- .github/workflows/CI_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index b39a7146..881dc22e 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -21,7 +21,7 @@ jobs: # run: cd ./example && mpirun -n 1 python3 -u sfsi_toy_gaussian.py - name: change directory and validate run: | - cd ./example/testing_folder && + cd example/testing_folder && output=$(python3 test_script.py) && if [[ "$output" == "0" ]]; then exit 1 From 09d1aa078129c839ae7a1e593b6e3c1a5465d2db Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 20:44:08 -0500 Subject: [PATCH 10/50] CI implement --- .github/workflows/CI_testing.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 881dc22e..70fd5818 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -10,18 +10,18 @@ defaults: jobs: style: runs-on: ubuntu-22.04 - # container: - # image: dolfinx/dolfinx:stable - # options: --user 1001 --privileged + container: + image: dolfinx/dolfinx:stable + options: --user 1001 --privileged name: CI test steps: - name: run code uses: actions/checkout@v2 - # - name: run the file - # run: cd ./example && mpirun -n 1 python3 -u sfsi_toy_gaussian.py + - name: run the file + run: cd ./example && mpirun -n 1 python3 -u sfsi_toy_gaussian.py - name: change directory and validate run: | - cd example/testing_folder && + cd ./example/testing_folder && output=$(python3 test_script.py) && if [[ "$output" == "0" ]]; then exit 1 From ab3aaf06b776fd32ab3cce240678d1d679bcae5f Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 20:47:32 -0500 Subject: [PATCH 11/50] testing script added --- example/testing_folder/test_script.py | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 example/testing_folder/test_script.py diff --git a/example/testing_folder/test_script.py b/example/testing_folder/test_script.py new file mode 100644 index 00000000..0acdd9b4 --- /dev/null +++ b/example/testing_folder/test_script.py @@ -0,0 +1,36 @@ +import numpy as np +import pickle + +with open('outputs.pickle', 'rb') as f: + data = pickle.load(f) + +xvals = data["xvals"] +arr_1 = data['arr_1'] +arr_2 = data['arr_2'] +value = data['sym_Hessian_value'] + +if(value > 1e-10): + print("1") + exit + +#values from arr_1 +relevant_values_1, relevant_values_2 = arr_1[10:], arr_2[10:] + +x = xvals[10:] + +data = relevant_values_1 +y = data +coefficients = np.polyfit(np.log(x), np.log(y), 1) +slope_1 = coefficients[0] + +data = relevant_values_2 +y = data +coefficients = np.polyfit(np.log(x), np.log(y), 1) +slope_2 = coefficients[0] + + +if(np.abs(slope_1 - 1) > 1e-1 or np.abs(slope_2 - 1) > 1e-1): + print("1") + exit + +print("0") From 4f7f69497638a7fe39b28c31a7b9c0ce85f8320a Mon Sep 17 00:00:00 2001 From: V-Rang Date: Wed, 13 Mar 2024 20:50:36 -0500 Subject: [PATCH 12/50] implement CI fix --- .github/workflows/CI_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 70fd5818..0e9ad2a5 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -23,6 +23,6 @@ jobs: run: | cd ./example/testing_folder && output=$(python3 test_script.py) && - if [[ "$output" == "0" ]]; then + if [[ "$output" == "1" ]]; then exit 1 fi \ No newline at end of file From 3f556ba4c7d11fd54995ad6557f585ec78e74991 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Thu, 14 Mar 2024 08:10:20 -0500 Subject: [PATCH 13/50] mpi hang not observed if plt.show() not last line --- example/poisson_example.py | 14 ++++---- example/sfsi_toy_gaussian.py | 55 +++++++++++++++---------------- hippylibX/modeling/modelVerify.py | 6 ---- 3 files changed, 34 insertions(+), 41 deletions(-) diff --git a/example/poisson_example.py b/example/poisson_example.py index 84bc33f7..646ee09c 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -108,16 +108,16 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict hpx.parRandom.normal(1.,noise) prior.sample(noise,m0) - hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - # eps, err_grad, err_H = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + eps, err_grad, err_H,_ = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - # if(rank == 0): - # print(err_grad,'\n') - # print(err_H) - # plt.show() + if(rank == 0): + print(err_grad,'\n') + print(err_H) + plt.show() # # ####################################### diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index 252bdb4b..882a0108 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -136,46 +136,45 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict with open('testing_folder/outputs.pickle','wb') as f: pickle.dump(data,f) - - # if(rank == 0): - # print(err_grad,'\n') - # print(err_H) - # plt.show() + if(rank == 0): + print(err_grad,'\n') + print(err_H) + plt.show() # ####################################### - # prior_mean_copy = prior.generate_parameter(0) - # prior_mean_copy.array[:] = prior_mean.array[:] + prior_mean_copy = prior.generate_parameter(0) + prior_mean_copy.array[:] = prior_mean.array[:] - # x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] - # if rank == 0: - # print( sep, "Find the MAP point", sep) + if rank == 0: + print( sep, "Find the MAP point", sep) - # parameters = hpx.ReducedSpaceNewtonCG_ParameterList() - # parameters["rel_tolerance"] = 1e-6 - # parameters["abs_tolerance"] = 1e-9 - # parameters["max_iter"] = 500 - # parameters["cg_coarse_tolerance"] = 5e-1 - # parameters["globalization"] = "LS" - # parameters["GN_iter"] = 20 - # if rank != 0: - # parameters["print_level"] = -1 + parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + parameters["rel_tolerance"] = 1e-6 + parameters["abs_tolerance"] = 1e-9 + parameters["max_iter"] = 500 + parameters["cg_coarse_tolerance"] = 5e-1 + parameters["globalization"] = "LS" + parameters["GN_iter"] = 20 + if rank != 0: + parameters["print_level"] = -1 - # solver = hpx.ReducedSpaceNewtonCG(model, parameters) + solver = hpx.ReducedSpaceNewtonCG(model, parameters) - # x = solver.solve(x) + x = solver.solve(x) - # if solver.converged: - # master_print(comm, "\nConverged in ", solver.it, " iterations.") - # else: - # master_print(comm, "\nNot Converged") + if solver.converged: + master_print(comm, "\nConverged in ", solver.it, " iterations.") + else: + master_print(comm, "\nNot Converged") - # master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) - # master_print (comm, "Final gradient norm: ", solver.final_grad_norm) - # master_print (comm, "Final cost: ", solver.final_cost) + master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + master_print (comm, "Final cost: ", solver.final_cost) ####################################### diff --git a/hippylibX/modeling/modelVerify.py b/hippylibX/modeling/modelVerify.py index a1dd288a..a58d2d44 100644 --- a/hippylibX/modeling/modelVerify.py +++ b/hippylibX/modeling/modelVerify.py @@ -45,12 +45,6 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr temp_petsc_vec_grad_x = dlx.la.create_petsc_vector_wrap(grad_x) - # temp_petsc_vec_h = dlx.la.create_petsc_vector_wrap(h) - - # grad_xh = temp_petsc_vec_grad_x.dot(temp_petsc_vec_h) - - - # temp_petsc_vec_h.destroy() model.setPointForHessianEvaluations(x) From 21d5b36b3baa3e455b3745f3c6bf45501bbfd389 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Thu, 14 Mar 2024 13:45:32 -0500 Subject: [PATCH 14/50] plt.show() not hanging with multiproc --- example/poisson_example.py | 1 + 1 file changed, 1 insertion(+) diff --git a/example/poisson_example.py b/example/poisson_example.py index 646ee09c..49afb547 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -108,6 +108,7 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict hpx.parRandom.normal(1.,noise) prior.sample(noise,m0) + # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) From 8e0e656d2967ee189b3ba4d250a2594b53333363 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Thu, 14 Mar 2024 19:06:32 -0500 Subject: [PATCH 15/50] possible memory issue needs to be fixed --- example/poisson_example.py | 91 +++++++---- example/sfsi_toy_gaussian.py | 88 +++++++---- hippylibX/modeling/modelVerify.py | 37 +++++ hippylibX/test/testing_suite_file.py | 221 +++++++++++++++++++++++++++ 4 files changed, 375 insertions(+), 62 deletions(-) create mode 100644 hippylibX/test/testing_suite_file.py diff --git a/example/poisson_example.py b/example/poisson_example.py index 49afb547..fd521134 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -7,7 +7,7 @@ import sys import os import dolfinx.fem.petsc - +import pickle from matplotlib import pyplot as plt @@ -112,46 +112,77 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + + + + # if(rank == 0): + # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + # if(comm.size == 1): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: + # pickle.dump(data,f) + + # if(comm.size == 4): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: + # pickle.dump(data,f) + + + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + + # if(rank == 0): + # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + + # if(comm.size == 1): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: + # pickle.dump(data,f) + + # if(comm.size == 4): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: + # pickle.dump(data,f) + - eps, err_grad, err_H,_ = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - if(rank == 0): - print(err_grad,'\n') - print(err_H) - plt.show() + # if(rank == 0): + # print(err_grad,'\n') + # print(err_H) + # plt.show() # # ####################################### - prior_mean_copy = prior.generate_parameter(0) - prior_mean_copy.array[:] = prior_mean.array[:] + # prior_mean_copy = prior.generate_parameter(0) + # prior_mean_copy.array[:] = prior_mean.array[:] - x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + # x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] - if rank == 0: - print( sep, "Find the MAP point", sep) + # if rank == 0: + # print( sep, "Find the MAP point", sep) - parameters = hpx.ReducedSpaceNewtonCG_ParameterList() - parameters["rel_tolerance"] = 1e-6 - parameters["abs_tolerance"] = 1e-9 - parameters["max_iter"] = 500 - parameters["cg_coarse_tolerance"] = 5e-1 - parameters["globalization"] = "LS" - parameters["GN_iter"] = 20 - if rank != 0: - parameters["print_level"] = -1 + # parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + # parameters["rel_tolerance"] = 1e-6 + # parameters["abs_tolerance"] = 1e-9 + # parameters["max_iter"] = 500 + # parameters["cg_coarse_tolerance"] = 5e-1 + # parameters["globalization"] = "LS" + # parameters["GN_iter"] = 20 + # if rank != 0: + # parameters["print_level"] = -1 - solver = hpx.ReducedSpaceNewtonCG(model, parameters) + # solver = hpx.ReducedSpaceNewtonCG(model, parameters) - x = solver.solve(x) + # x = solver.solve(x) - if solver.converged: - master_print(comm, "\nConverged in ", solver.it, " iterations.") - else: - master_print(comm, "\nNot Converged") - - master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) - master_print (comm, "Final gradient norm: ", solver.final_grad_norm) - master_print (comm, "Final cost: ", solver.final_cost) + # if solver.converged: + # master_print(comm, "\nConverged in ", solver.it, " iterations.") + # else: + # master_print(comm, "\nNot Converged") + + # master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + # master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + # master_print (comm, "Final cost: ", solver.final_cost) ####################################### diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index 882a0108..7b6e473a 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -127,54 +127,78 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict hpx.parRandom.normal(1.,noise) prior.sample(noise,m0) - hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - eps, err_grad, err_H, rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + if(rank == 0): - data = {"xvals":eps,"arr_1":err_grad, "arr_2": err_H, "sym_Hessian_value":rel_symm_error} - os.makedirs('testing_folder',exist_ok=True) - with open('testing_folder/outputs.pickle','wb') as f: - pickle.dump(data,f) + data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + if(comm.size == 1): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_qpact_1_proc_misfit_True.pickle','wb') as f: + pickle.dump(data,f) + if(comm.size == 4): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_qpact_4_proc_misfit_True.pickle','wb') as f: + pickle.dump(data,f) + + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + if(rank == 0): - print(err_grad,'\n') - print(err_H) - plt.show() + data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + + if(comm.size == 1): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_qpact_1_proc_misfit_False.pickle','wb') as f: + pickle.dump(data,f) + + if(comm.size == 4): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_qpact_4_proc_misfit_False.pickle','wb') as f: + pickle.dump(data,f) + + + + + # if(rank == 0): + # print(err_grad,'\n') + # print(err_H) + # plt.show() # ####################################### - prior_mean_copy = prior.generate_parameter(0) - prior_mean_copy.array[:] = prior_mean.array[:] + # prior_mean_copy = prior.generate_parameter(0) + # prior_mean_copy.array[:] = prior_mean.array[:] - x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + # x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] - if rank == 0: - print( sep, "Find the MAP point", sep) + # if rank == 0: + # print( sep, "Find the MAP point", sep) - parameters = hpx.ReducedSpaceNewtonCG_ParameterList() - parameters["rel_tolerance"] = 1e-6 - parameters["abs_tolerance"] = 1e-9 - parameters["max_iter"] = 500 - parameters["cg_coarse_tolerance"] = 5e-1 - parameters["globalization"] = "LS" - parameters["GN_iter"] = 20 - if rank != 0: - parameters["print_level"] = -1 + # parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + # parameters["rel_tolerance"] = 1e-6 + # parameters["abs_tolerance"] = 1e-9 + # parameters["max_iter"] = 500 + # parameters["cg_coarse_tolerance"] = 5e-1 + # parameters["globalization"] = "LS" + # parameters["GN_iter"] = 20 + # if rank != 0: + # parameters["print_level"] = -1 - solver = hpx.ReducedSpaceNewtonCG(model, parameters) + # solver = hpx.ReducedSpaceNewtonCG(model, parameters) - x = solver.solve(x) + # x = solver.solve(x) - if solver.converged: - master_print(comm, "\nConverged in ", solver.it, " iterations.") - else: - master_print(comm, "\nNot Converged") + # if solver.converged: + # master_print(comm, "\nConverged in ", solver.it, " iterations.") + # else: + # master_print(comm, "\nNot Converged") - master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) - master_print (comm, "Final gradient norm: ", solver.final_grad_norm) - master_print (comm, "Final cost: ", solver.final_cost) + # master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + # master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + # master_print (comm, "Final cost: ", solver.final_cost) ####################################### diff --git a/hippylibX/modeling/modelVerify.py b/hippylibX/modeling/modelVerify.py index a58d2d44..8fef53bc 100644 --- a/hippylibX/modeling/modelVerify.py +++ b/hippylibX/modeling/modelVerify.py @@ -122,7 +122,44 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr print( "(yy, H xx) - (xx, H yy) = ", rel_symm_error) if rel_symm_error > 1e-10: print( "HESSIAN IS NOT SYMMETRIC!!") + + + #clean-up ops + if(model.problem.A is not None): + model.problem.A.destroy() + model.problem.A = None + + + if(model.problem.At is not None): + model.problem.At.destroy() + model.problem.At = None + if(model.problem.C is not None): + model.problem.C.destroy() + model.problem.C = None + + + if(model.problem.Wuu is not None): + model.problem.Wuu.destroy() + model.problem.Wuu = None + + + if(model.problem.Wmu is not None): + model.problem.Wmu.destroy() + model.problem.Wmu = None + + + if(model.problem.Wum is not None): + model.problem.Wum.destroy() + model.problem.Wum = None + + + if(model.problem.Wmm is not None): + model.problem.Wmm.destroy() + model.problem.Wmm = None + + + return eps, err_grad, err_H, rel_symm_error diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py new file mode 100644 index 00000000..94082f05 --- /dev/null +++ b/hippylibX/test/testing_suite_file.py @@ -0,0 +1,221 @@ +# import unittest +# import subprocess +# import sys +# import os + +# sys.path.append(os.path.abspath('../..')) + +# import hippylibX as hpx +# sys.path.append(os.path.abspath('../../example')) + +# from poisson_example import run_inversion + +# class TestFile1Execution(unittest.TestCase): +# os.chdir("../../example/") +# def test_execution_poisson_single(self): +# command = "mpirun -n 1 python3 -u poisson_example.py" +# return_code = os.system(command) +# self.assertEqual(return_code, 0, "Error running 1 proc poisson_example.py") + +# if __name__ == "__main__": +# unittest.main() + +################################################################### + + +# import unittest +# from unittest.mock import patch +# import sys +# import os + +# sys.path.append(os.path.abspath('../..')) + +# import hippylibX +# from hippylibX import modelVerify + +# sys.path.append(os.path.abspath('../../example')) + +# from poisson_example import run_inversion + + + +# class TestFileExecution(unittest.TestCase): +# @patch('hippylibX.modelVerify') +# def test_verify_function_called(self, mock_verify): +# # Mock the verify function +# mock_verify.return_value = (1, 2, 3, 4) + +# nx = 64 +# ny = 64 +# noise_variance = 1e-4 +# prior_param = {"gamma": 0.1, "delta": 1.} +# result = run_inversion(nx, ny, noise_variance, prior_param) + +# # Verify that the verify function was called +# mock_verify.assert_called_once() + +# # Perform assertions on the returned values +# print(result) +# # var1, var2, var3, var4 = result +# # self.assertTrue(var1 + var2 == 3, "var1 + var2 should be equal to 3") +# # self.assertTrue(var3 * var4 == 12, "var3 * var4 should be equal to 12") + +# if __name__ == "__main__": +# unittest.main() + + +################################################################### + +import unittest +import sys +import os +import numpy as np +import pickle + + +sys.path.append(os.path.abspath('../..')) + +import hippylibX as hpx +sys.path.append(os.path.abspath('../../example')) + + +def data_parser(data): + eps = data["eps"] + err_grad = data['err_grad'] + err_H = data['err_H'] + sym_Hessian_value = data['sym_Hessian_value'] + + slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) + slope_grad = slope_grad_coeffs[0] + + slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) + slope_H = slope_H_coeffs[0] + + return sym_Hessian_value, slope_grad, slope_H + + +class Testing_Execution(unittest.TestCase): + + def test_qpact_execution_serial(self): + pwd = os.getcwd() + os.chdir("../../example/") + command = "mpirun -n 1 python3 -u sfsi_toy_gaussian.py" + return_val = os.system(command) + os.chdir(pwd) + self.assertEqual(return_val, 0, "1 proc qpact: Error running") + + def test_qpact_execution_parallel(self): + pwd = os.getcwd() + os.chdir("../../example/") + command = "mpirun -n 4 python3 -u sfsi_toy_gaussian.py" + return_val = os.system(command) + os.chdir(pwd) + self.assertEqual(return_val, 0, "4 proc qpact: Error running") + + def test_poisson_execution_serial(self): + pwd = os.getcwd() + os.chdir("../../example/") + command = "mpirun -n 1 python3 -u poisson_example.py" + return_val = os.system(command) + os.chdir(pwd) + self.assertEqual(return_val, 0, "1 proc poisson: Error running") + + def test_poisson_execution_parallel(self): + pwd = os.getcwd() + os.chdir("../../example/") + command = "mpirun -n 4 python3 -u poisson_example.py" + return_val = os.system(command) + os.chdir(pwd) + self.assertEqual(return_val, 0, "4 proc poisson: Error running") + + + # def test_qpact_results_serial_misfit_True(self): + + # with open('outputs_qpact_1_proc_misfit_True.pickle', 'rb') as f: + # data = pickle.load(f) + + # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + + # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc qpact misfit True: Symmetric Hessian check value is greater than 1e-10") + # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc qpact misfit True: FD Gradient check slope is not close to 1") + # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc qpact misfit True: FD Hessian check slope is not close to 1") + + # def test_qpact_results_serial_misfit_False(self): + # #misfit=False + # with open('outputs_qpact_1_proc_misfit_False.pickle', 'rb') as f: + # data = pickle.load(f) + # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc qpact misfit False: Symmetric Hessian check value is greater than 1e-10") + # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc qpact misfit False: FD Gradient check slope is not close to 1") + # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc qpact misfit False: FD Hessian check slope is not close to 1") + + # def test_qpact_results_parallel_misfit_True(self): + + # with open('outputs_qpact_4_proc_misfit_True.pickle', 'rb') as f: + # data = pickle.load(f) + + # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + + # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc qpact misfit True: Symmetric Hessian check value is greater than 1e-10") + # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc qpact misfit True: FD Gradient check slope is not close to 1") + # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc qpact misfit True: FD Hessian check slope is not close to 1") + + # def test_qpact_results_parallel_misfit_False(self): + # #misfit=False + # with open('outputs_qpact_4_proc_misfit_False.pickle', 'rb') as f: + # data = pickle.load(f) + # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc qpact misfit False: Symmetric Hessian check value is greater than 1e-10") + # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc qpact misfit False: FD Gradient check slope is not close to 1") + # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc qpact misfit False: FD Hessian check slope is not close to 1") + + + # def test_poisson_results_serial_misfit_True(self): + + # with open('outputs_poisson_1_proc_misfit_True.pickle', 'rb') as f: + # data = pickle.load(f) + + # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + + # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc poisson misfit True: Symmetric Hessian check value is greater than 1e-10") + # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc poisson misfit True: FD Gradient check slope is not close to 1") + # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc poisson misfit True: FD Hessian check slope is not close to 1") + + # def test_poisson_results_serial_misfit_False(self): + # #misfit=False + # with open('outputs_poisson_1_proc_misfit_False.pickle', 'rb') as f: + # data = pickle.load(f) + # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc poisson misfit False: Symmetric Hessian check value is greater than 1e-10") + # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc poisson misfit False: FD Gradient check slope is not close to 1") + # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc poisson misfit False: FD Hessian check slope is not close to 1") + + # def test_poisson_results_parallel_misfit_True(self): + # with open('outputs_poisson_4_proc_misfit_True.pickle', 'rb') as f: + # data = pickle.load(f) + + # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + + # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc poisson misfit True: Symmetric Hessian check value is greater than 1e-10") + # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc poisson misfit True: FD Gradient check slope is not close to 1") + # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc poisson misfit True: FD Hessian check slope is not close to 1") + + # def test_qpact_results_parallel_misfit_False(self): + # #misfit=False + # with open('outputs_poisson_4_proc_misfit_False.pickle', 'rb') as f: + # data = pickle.load(f) + # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc poisson misfit False: Symmetric Hessian check value is greater than 1e-10") + # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc poisson misfit False: FD Gradient check slope is not close to 1") + # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc poisson misfit False: FD Hessian check slope is not close to 1") + + + +if __name__ == "__main__": + unittest.main() + + + + + + From 51ae57783e866720935c45fac7046c94d96f6336 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Thu, 14 Mar 2024 19:07:49 -0500 Subject: [PATCH 16/50] testing suite comments removed --- hippylibX/test/testing_suite_file.py | 67 ---------------------------- 1 file changed, 67 deletions(-) diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 94082f05..2188bcdc 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -1,70 +1,3 @@ -# import unittest -# import subprocess -# import sys -# import os - -# sys.path.append(os.path.abspath('../..')) - -# import hippylibX as hpx -# sys.path.append(os.path.abspath('../../example')) - -# from poisson_example import run_inversion - -# class TestFile1Execution(unittest.TestCase): -# os.chdir("../../example/") -# def test_execution_poisson_single(self): -# command = "mpirun -n 1 python3 -u poisson_example.py" -# return_code = os.system(command) -# self.assertEqual(return_code, 0, "Error running 1 proc poisson_example.py") - -# if __name__ == "__main__": -# unittest.main() - -################################################################### - - -# import unittest -# from unittest.mock import patch -# import sys -# import os - -# sys.path.append(os.path.abspath('../..')) - -# import hippylibX -# from hippylibX import modelVerify - -# sys.path.append(os.path.abspath('../../example')) - -# from poisson_example import run_inversion - - - -# class TestFileExecution(unittest.TestCase): -# @patch('hippylibX.modelVerify') -# def test_verify_function_called(self, mock_verify): -# # Mock the verify function -# mock_verify.return_value = (1, 2, 3, 4) - -# nx = 64 -# ny = 64 -# noise_variance = 1e-4 -# prior_param = {"gamma": 0.1, "delta": 1.} -# result = run_inversion(nx, ny, noise_variance, prior_param) - -# # Verify that the verify function was called -# mock_verify.assert_called_once() - -# # Perform assertions on the returned values -# print(result) -# # var1, var2, var3, var4 = result -# # self.assertTrue(var1 + var2 == 3, "var1 + var2 should be equal to 3") -# # self.assertTrue(var3 * var4 == 12, "var3 * var4 should be equal to 12") - -# if __name__ == "__main__": -# unittest.main() - - -################################################################### import unittest import sys From dee84e5af9f152b8165d7aef92d18fe564f0ae29 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Thu, 14 Mar 2024 19:17:30 -0500 Subject: [PATCH 17/50] possible memory issue --- example/poisson_example.py | 44 ++++++++++++++-------------- hippylibX/test/testing_suite_file.py | 27 +++++++++-------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/example/poisson_example.py b/example/poisson_example.py index fd521134..ad7cc391 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -116,33 +116,33 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict - # if(rank == 0): - # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - # if(comm.size == 1): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: - # pickle.dump(data,f) - - # if(comm.size == 4): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: - # pickle.dump(data,f) + if(rank == 0): + data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + if(comm.size == 1): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: + pickle.dump(data,f) + + if(comm.size == 4): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: + pickle.dump(data,f) eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - # if(rank == 0): - # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + if(rank == 0): + data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - # if(comm.size == 1): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: - # pickle.dump(data,f) - - # if(comm.size == 4): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: - # pickle.dump(data,f) + if(comm.size == 1): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: + pickle.dump(data,f) + + if(comm.size == 4): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: + pickle.dump(data,f) diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 2188bcdc..dd1fc822 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -29,39 +29,40 @@ def data_parser(data): class Testing_Execution(unittest.TestCase): - def test_qpact_execution_serial(self): + def test_poisson_execution_serial(self): pwd = os.getcwd() os.chdir("../../example/") - command = "mpirun -n 1 python3 -u sfsi_toy_gaussian.py" + command = "mpirun -n 1 python3 -u poisson_example.py" return_val = os.system(command) os.chdir(pwd) - self.assertEqual(return_val, 0, "1 proc qpact: Error running") + self.assertEqual(return_val, 0, "1 proc poisson: Error running") - def test_qpact_execution_parallel(self): + def test_poisson_execution_parallel(self): pwd = os.getcwd() os.chdir("../../example/") - command = "mpirun -n 4 python3 -u sfsi_toy_gaussian.py" + command = "mpirun -n 4 python3 -u poisson_example.py" return_val = os.system(command) os.chdir(pwd) - self.assertEqual(return_val, 0, "4 proc qpact: Error running") + self.assertEqual(return_val, 0, "4 proc poisson: Error running") - def test_poisson_execution_serial(self): + def test_qpact_execution_serial(self): pwd = os.getcwd() os.chdir("../../example/") - command = "mpirun -n 1 python3 -u poisson_example.py" + command = "mpirun -n 1 python3 -u sfsi_toy_gaussian.py" return_val = os.system(command) os.chdir(pwd) - self.assertEqual(return_val, 0, "1 proc poisson: Error running") + self.assertEqual(return_val, 0, "1 proc qpact: Error running") - def test_poisson_execution_parallel(self): + def test_qpact_execution_parallel(self): pwd = os.getcwd() os.chdir("../../example/") - command = "mpirun -n 4 python3 -u poisson_example.py" + command = "mpirun -n 4 python3 -u sfsi_toy_gaussian.py" return_val = os.system(command) os.chdir(pwd) - self.assertEqual(return_val, 0, "4 proc poisson: Error running") + self.assertEqual(return_val, 0, "4 proc qpact: Error running") + + - # def test_qpact_results_serial_misfit_True(self): # with open('outputs_qpact_1_proc_misfit_True.pickle', 'rb') as f: From 20bec60e848bed5def8e63676972bc410c2c7afd Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 08:03:03 -0500 Subject: [PATCH 18/50] investigatingmemory usage --- example/poisson_example.py | 50 +++---- hippylibX/modeling/PDEProblem.py | 7 + hippylibX/modeling/modelVerify.py | 46 +++--- hippylibX/test/testing_suite_file.py | 126 ++++++++--------- misc/mem_usage.py | 60 ++++++++ misc/poisson_example_copy.py | 202 +++++++++++++++++++++++++++ 6 files changed, 380 insertions(+), 111 deletions(-) create mode 100644 misc/mem_usage.py create mode 100644 misc/poisson_example_copy.py diff --git a/example/poisson_example.py b/example/poisson_example.py index ad7cc391..5905c708 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -8,6 +8,8 @@ import os import dolfinx.fem.petsc import pickle +from memory_profiler import profile + from matplotlib import pyplot as plt @@ -41,7 +43,7 @@ def __init__(self, d : float, sigma2 : float): def __call__(self, u : dlx.fem.Function, m: dlx.fem.Function) -> ufl.form.Form: return .5/self.sigma2*ufl.inner(u - self.d, u - self.d)*self.dx - +@profile def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: sep = "\n"+"#"*80+"\n" @@ -116,33 +118,33 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict - if(rank == 0): - data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - if(comm.size == 1): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: - pickle.dump(data,f) - - if(comm.size == 4): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: - pickle.dump(data,f) + # if(rank == 0): + # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + # if(comm.size == 1): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: + # pickle.dump(data,f) + + # if(comm.size == 4): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: + # pickle.dump(data,f) - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + # eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - if(rank == 0): - data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + # if(rank == 0): + # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - if(comm.size == 1): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: - pickle.dump(data,f) - - if(comm.size == 4): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: - pickle.dump(data,f) + # if(comm.size == 1): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: + # pickle.dump(data,f) + + # if(comm.size == 4): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: + # pickle.dump(data,f) diff --git a/hippylibX/modeling/PDEProblem.py b/hippylibX/modeling/PDEProblem.py index ae57de89..10b93ee8 100644 --- a/hippylibX/modeling/PDEProblem.py +++ b/hippylibX/modeling/PDEProblem.py @@ -238,8 +238,15 @@ def setLinearizationPoint(self,x : list, gauss_newton_approx) -> None: self.solver_adj_inc.setOperators(self.At) if gauss_newton_approx: + if(self.Wuu is not None): + self.Wuu.destroy() self.Wuu = None + if(self.Wmu is not None): + self.Wmu.destroy() self.Wmu = None + + if(self.Wmm is not None): + self.Wmm.destroy() self.Wmm = None else: diff --git a/hippylibX/modeling/modelVerify.py b/hippylibX/modeling/modelVerify.py index 8fef53bc..6a18a411 100644 --- a/hippylibX/modeling/modelVerify.py +++ b/hippylibX/modeling/modelVerify.py @@ -93,8 +93,8 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr err_H[i] = err.norm(petsc4py.PETSc.NormType.NORM_INFINITY) err.destroy() - if verbose: - modelVerifyPlotErrors(comm, misfit_only,is_quadratic, eps, err_grad, err_H) + # if verbose: + # modelVerifyPlotErrors(comm, misfit_only,is_quadratic, eps, err_grad, err_H) #comm and misfit_only are being passed for plotting purposes only- #the title of the plot for saved figures. #has to be removed for the final version. @@ -125,38 +125,38 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr #clean-up ops - if(model.problem.A is not None): - model.problem.A.destroy() - model.problem.A = None + # if(model.problem.A is not None): + # model.problem.A.destroy() + # model.problem.A = None - if(model.problem.At is not None): - model.problem.At.destroy() - model.problem.At = None + # if(model.problem.At is not None): + # model.problem.At.destroy() + # model.problem.At = None - if(model.problem.C is not None): - model.problem.C.destroy() - model.problem.C = None + # if(model.problem.C is not None): + # model.problem.C.destroy() + # model.problem.C = None - if(model.problem.Wuu is not None): - model.problem.Wuu.destroy() - model.problem.Wuu = None + # if(model.problem.Wuu is not None): + # model.problem.Wuu.destroy() + # model.problem.Wuu = None - if(model.problem.Wmu is not None): - model.problem.Wmu.destroy() - model.problem.Wmu = None + # if(model.problem.Wmu is not None): + # model.problem.Wmu.destroy() + # model.problem.Wmu = None - if(model.problem.Wum is not None): - model.problem.Wum.destroy() - model.problem.Wum = None + # if(model.problem.Wum is not None): + # model.problem.Wum.destroy() + # model.problem.Wum = None - if(model.problem.Wmm is not None): - model.problem.Wmm.destroy() - model.problem.Wmm = None + # if(model.problem.Wmm is not None): + # model.problem.Wmm.destroy() + # model.problem.Wmm = None diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index dd1fc822..5f9a4348 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -40,7 +40,7 @@ def test_poisson_execution_serial(self): def test_poisson_execution_parallel(self): pwd = os.getcwd() os.chdir("../../example/") - command = "mpirun -n 4 python3 -u poisson_example.py" + command = "mpirun -n 1 python3 -u poisson_example.py" return_val = os.system(command) os.chdir(pwd) self.assertEqual(return_val, 0, "4 proc poisson: Error running") @@ -56,92 +56,90 @@ def test_qpact_execution_serial(self): def test_qpact_execution_parallel(self): pwd = os.getcwd() os.chdir("../../example/") - command = "mpirun -n 4 python3 -u sfsi_toy_gaussian.py" + command = "mpirun -n 1 python3 -u sfsi_toy_gaussian.py" return_val = os.system(command) os.chdir(pwd) self.assertEqual(return_val, 0, "4 proc qpact: Error running") + def test_qpact_results_serial_misfit_True(self): + with open('outputs_qpact_1_proc_misfit_True.pickle', 'rb') as f: + data = pickle.load(f) - # def test_qpact_results_serial_misfit_True(self): - - # with open('outputs_qpact_1_proc_misfit_True.pickle', 'rb') as f: - # data = pickle.load(f) - - # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + sym_Hessian_value, slope_grad, slope_H = data_parser(data) - # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc qpact misfit True: Symmetric Hessian check value is greater than 1e-10") - # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc qpact misfit True: FD Gradient check slope is not close to 1") - # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc qpact misfit True: FD Hessian check slope is not close to 1") + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc qpact misfit True: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc qpact misfit True: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc qpact misfit True: FD Hessian check slope is not close to 1") - # def test_qpact_results_serial_misfit_False(self): - # #misfit=False - # with open('outputs_qpact_1_proc_misfit_False.pickle', 'rb') as f: - # data = pickle.load(f) - # sym_Hessian_value, slope_grad, slope_H = data_parser(data) - # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc qpact misfit False: Symmetric Hessian check value is greater than 1e-10") - # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc qpact misfit False: FD Gradient check slope is not close to 1") - # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc qpact misfit False: FD Hessian check slope is not close to 1") + def test_qpact_results_serial_misfit_False(self): + #misfit=False + with open('outputs_qpact_1_proc_misfit_False.pickle', 'rb') as f: + data = pickle.load(f) + sym_Hessian_value, slope_grad, slope_H = data_parser(data) + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc qpact misfit False: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc qpact misfit False: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc qpact misfit False: FD Hessian check slope is not close to 1") - # def test_qpact_results_parallel_misfit_True(self): + def test_qpact_results_parallel_misfit_True(self): - # with open('outputs_qpact_4_proc_misfit_True.pickle', 'rb') as f: - # data = pickle.load(f) + with open('outputs_qpact_4_proc_misfit_True.pickle', 'rb') as f: + data = pickle.load(f) - # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + sym_Hessian_value, slope_grad, slope_H = data_parser(data) - # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc qpact misfit True: Symmetric Hessian check value is greater than 1e-10") - # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc qpact misfit True: FD Gradient check slope is not close to 1") - # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc qpact misfit True: FD Hessian check slope is not close to 1") + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc qpact misfit True: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc qpact misfit True: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc qpact misfit True: FD Hessian check slope is not close to 1") - # def test_qpact_results_parallel_misfit_False(self): - # #misfit=False - # with open('outputs_qpact_4_proc_misfit_False.pickle', 'rb') as f: - # data = pickle.load(f) - # sym_Hessian_value, slope_grad, slope_H = data_parser(data) - # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc qpact misfit False: Symmetric Hessian check value is greater than 1e-10") - # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc qpact misfit False: FD Gradient check slope is not close to 1") - # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc qpact misfit False: FD Hessian check slope is not close to 1") + def test_qpact_results_parallel_misfit_False(self): + #misfit=False + with open('outputs_qpact_4_proc_misfit_False.pickle', 'rb') as f: + data = pickle.load(f) + sym_Hessian_value, slope_grad, slope_H = data_parser(data) + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc qpact misfit False: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc qpact misfit False: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc qpact misfit False: FD Hessian check slope is not close to 1") - # def test_poisson_results_serial_misfit_True(self): + def test_poisson_results_serial_misfit_True(self): - # with open('outputs_poisson_1_proc_misfit_True.pickle', 'rb') as f: - # data = pickle.load(f) + with open('outputs_poisson_1_proc_misfit_True.pickle', 'rb') as f: + data = pickle.load(f) - # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + sym_Hessian_value, slope_grad, slope_H = data_parser(data) - # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc poisson misfit True: Symmetric Hessian check value is greater than 1e-10") - # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc poisson misfit True: FD Gradient check slope is not close to 1") - # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc poisson misfit True: FD Hessian check slope is not close to 1") + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc poisson misfit True: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc poisson misfit True: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc poisson misfit True: FD Hessian check slope is not close to 1") - # def test_poisson_results_serial_misfit_False(self): - # #misfit=False - # with open('outputs_poisson_1_proc_misfit_False.pickle', 'rb') as f: - # data = pickle.load(f) - # sym_Hessian_value, slope_grad, slope_H = data_parser(data) - # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc poisson misfit False: Symmetric Hessian check value is greater than 1e-10") - # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc poisson misfit False: FD Gradient check slope is not close to 1") - # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc poisson misfit False: FD Hessian check slope is not close to 1") + def test_poisson_results_serial_misfit_False(self): + #misfit=False + with open('outputs_poisson_1_proc_misfit_False.pickle', 'rb') as f: + data = pickle.load(f) + sym_Hessian_value, slope_grad, slope_H = data_parser(data) + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc poisson misfit False: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc poisson misfit False: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc poisson misfit False: FD Hessian check slope is not close to 1") - # def test_poisson_results_parallel_misfit_True(self): - # with open('outputs_poisson_4_proc_misfit_True.pickle', 'rb') as f: - # data = pickle.load(f) + def test_poisson_results_parallel_misfit_True(self): + with open('outputs_poisson_4_proc_misfit_True.pickle', 'rb') as f: + data = pickle.load(f) - # sym_Hessian_value, slope_grad, slope_H = data_parser(data) + sym_Hessian_value, slope_grad, slope_H = data_parser(data) - # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc poisson misfit True: Symmetric Hessian check value is greater than 1e-10") - # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc poisson misfit True: FD Gradient check slope is not close to 1") - # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc poisson misfit True: FD Hessian check slope is not close to 1") + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc poisson misfit True: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc poisson misfit True: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc poisson misfit True: FD Hessian check slope is not close to 1") - # def test_qpact_results_parallel_misfit_False(self): - # #misfit=False - # with open('outputs_poisson_4_proc_misfit_False.pickle', 'rb') as f: - # data = pickle.load(f) - # sym_Hessian_value, slope_grad, slope_H = data_parser(data) - # self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc poisson misfit False: Symmetric Hessian check value is greater than 1e-10") - # self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc poisson misfit False: FD Gradient check slope is not close to 1") - # self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc poisson misfit False: FD Hessian check slope is not close to 1") + def test_qpact_results_parallel_misfit_False(self): + #misfit=False + with open('outputs_poisson_4_proc_misfit_False.pickle', 'rb') as f: + data = pickle.load(f) + sym_Hessian_value, slope_grad, slope_H = data_parser(data) + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc poisson misfit False: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc poisson misfit False: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc poisson misfit False: FD Hessian check slope is not close to 1") diff --git a/misc/mem_usage.py b/misc/mem_usage.py new file mode 100644 index 00000000..c1eea8ee --- /dev/null +++ b/misc/mem_usage.py @@ -0,0 +1,60 @@ +##Don't push this to github + +import matplotlib.pyplot as plt + +import time +import os +import psutil + +import poisson_example_copy + +def elapsed_since(start): + return time.strftime("%H:%M:%S", time.gmtime(time.time() - start)) + + +def get_process_memory(): + process = psutil.Process(os.getpid()) + mem_info = process.memory_full_info() + return mem_info.uss + + + +def profile(func): + def wrapper(*args, **kwargs): + mem_before = get_process_memory() + start = time.time() + result = func(*args, **kwargs) + elapsed_time = elapsed_since(start) + mem_after = get_process_memory() + return (mem_after - mem_before)/1e6 + return wrapper + +nx = 64 +ny = 64 +noise_variance = 1e-4 +prior_param = {"gamma": 0.1, "delta": 1.} + +mem_usage = [] + + +value = profile(poisson_example_copy.run_inversion) + +num_calls = 100 + +for _ in range(num_calls): + mem_usage.append(value(nx, ny, noise_variance, prior_param)) + +plt.plot(mem_usage) + + +# # print(mem_usage) +plt.xlabel('Iteration') +plt.ylabel('Memory Usage (MB)') +plt.title('Memory Usage Over Iterations for poisson example') +plt.savefig("memory_usage_plot_poisson.png") +plt.show() + + + + +# print(value.wrapper) \ No newline at end of file diff --git a/misc/poisson_example_copy.py b/misc/poisson_example_copy.py new file mode 100644 index 00000000..5905c708 --- /dev/null +++ b/misc/poisson_example_copy.py @@ -0,0 +1,202 @@ +import ufl +import dolfinx as dlx +from mpi4py import MPI +import numpy as np +import petsc4py + +import sys +import os +import dolfinx.fem.petsc +import pickle +from memory_profiler import profile + + +from matplotlib import pyplot as plt + +sys.path.append( os.environ.get('HIPPYLIBX_BASE_DIR', "../") ) + +import hippylibX as hpx + +def master_print(comm, *args, **kwargs): + if comm.rank == 0: + print(*args, **kwargs) + +class Poisson_Approximation: + def __init__(self, alpha : float, f : float): + + self.alpha = alpha + self.f = f + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + self.ds = ufl.Measure("ds",metadata={"quadrature_degree":4}) + + def __call__(self, u: dlx.fem.Function, m : dlx.fem.Function, p : dlx.fem.Function) -> ufl.form.Form: + return ufl.exp(m) * ufl.inner(ufl.grad(u), ufl.grad(p))*self.dx + \ + self.alpha * ufl.inner(u,p)*self.ds - self.f*p*self.dx + + +class PoissonMisfitForm: + def __init__(self, d : float, sigma2 : float): + self.d = d + self.sigma2 = sigma2 + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + + def __call__(self, u : dlx.fem.Function, m: dlx.fem.Function) -> ufl.form.Form: + return .5/self.sigma2*ufl.inner(u - self.d, u - self.d)*self.dx + +@profile +def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: + sep = "\n"+"#"*80+"\n" + + comm = MPI.COMM_WORLD + rank = comm.rank + nproc = comm.size + + msh = dlx.mesh.create_unit_square(comm, nx, ny) + + Vh_phi = dlx.fem.FunctionSpace(msh, ("CG", 2)) + Vh_m = dlx.fem.FunctionSpace(msh, ("CG", 1)) + Vh = [Vh_phi, Vh_m, Vh_phi] + + ndofs = [Vh_phi.dofmap.index_map.size_global * Vh_phi.dofmap.index_map_bs, Vh_m.dofmap.index_map.size_global * Vh_m.dofmap.index_map_bs ] + master_print (comm, sep, "Set up the mesh and finite element spaces", sep) + master_print (comm, "Number of dofs: STATE={0}, PARAMETER={1}".format(*ndofs) ) + + # FORWARD MODEL + alpha = 100. + f = 1. + pde_handler = Poisson_Approximation(alpha, f) + pde = hpx.PDEVariationalProblem(Vh, pde_handler, [], [], is_fwd_linear=True) + + # GROUND TRUTH + m_true = dlx.fem.Function(Vh_m) + + m_true.interpolate(lambda x: np.log(2 + 7*( ( (x[0] - 0.5)**2 + (x[1] - 0.5)**2)**0.5 > 0.2)) ) + m_true.x.scatter_forward() + + m_true = m_true.x + + u_true = pde.generate_state() + + x_true = [u_true, m_true, None] + + pde.solveFwd(u_true,x_true) + + xfun = [dlx.fem.Function(Vhi) for Vhi in Vh] + + # LIKELIHOOD + hpx.updateFromVector(xfun[hpx.STATE],u_true) + u_fun_true = xfun[hpx.STATE] + + hpx.updateFromVector(xfun[hpx.PARAMETER],m_true) + m_fun_true = xfun[hpx.PARAMETER] + + d = dlx.fem.Function(Vh[hpx.STATE]) + d.x.array[:] = u_true.array[:] + hpx.parRandom.normal_perturb(np.sqrt(noise_variance),d.x) + d.x.scatter_forward() + + misfit_form = PoissonMisfitForm(d,noise_variance) + misfit = hpx.NonGaussianContinuousMisfit(msh, Vh, misfit_form) + + prior_mean = dlx.fem.Function(Vh_m) + prior_mean.x.array[:] = 0.01 + prior_mean = prior_mean.x + + prior = hpx.BiLaplacianPrior(Vh_m,prior_param["gamma"],prior_param["delta"],mean = prior_mean) + model = hpx.Model(pde, prior, misfit) + + noise = prior.generate_parameter("noise") + m0 = prior.generate_parameter(0) + hpx.parRandom.normal(1.,noise) + prior.sample(noise,m0) + + + # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + + + + # if(rank == 0): + # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + # if(comm.size == 1): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: + # pickle.dump(data,f) + + # if(comm.size == 4): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: + # pickle.dump(data,f) + + + # eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + + # if(rank == 0): + # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + + # if(comm.size == 1): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: + # pickle.dump(data,f) + + # if(comm.size == 4): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: + # pickle.dump(data,f) + + + + # if(rank == 0): + # print(err_grad,'\n') + # print(err_H) + # plt.show() + + # # ####################################### + + # prior_mean_copy = prior.generate_parameter(0) + # prior_mean_copy.array[:] = prior_mean.array[:] + + # x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + + # if rank == 0: + # print( sep, "Find the MAP point", sep) + + # parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + # parameters["rel_tolerance"] = 1e-6 + # parameters["abs_tolerance"] = 1e-9 + # parameters["max_iter"] = 500 + # parameters["cg_coarse_tolerance"] = 5e-1 + # parameters["globalization"] = "LS" + # parameters["GN_iter"] = 20 + # if rank != 0: + # parameters["print_level"] = -1 + + # solver = hpx.ReducedSpaceNewtonCG(model, parameters) + + # x = solver.solve(x) + + # if solver.converged: + # master_print(comm, "\nConverged in ", solver.it, " iterations.") + # else: + # master_print(comm, "\nNot Converged") + + # master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + # master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + # master_print (comm, "Final cost: ", solver.final_cost) + + ####################################### + + + + +if __name__ == "__main__": + nx = 64 + ny = 64 + noise_variance = 1e-4 + prior_param = {"gamma": 0.1, "delta": 1.} + run_inversion(nx, ny, noise_variance, prior_param) + + + From 9906551713ed828a8d3a19e4d1da14a78ccf7e46 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 08:06:51 -0500 Subject: [PATCH 19/50] updated testing suite file --- hippylibX/test/testing_suite_file.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 5f9a4348..11d08d64 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -40,7 +40,7 @@ def test_poisson_execution_serial(self): def test_poisson_execution_parallel(self): pwd = os.getcwd() os.chdir("../../example/") - command = "mpirun -n 1 python3 -u poisson_example.py" + command = "mpirun -n 4 python3 -u poisson_example.py" return_val = os.system(command) os.chdir(pwd) self.assertEqual(return_val, 0, "4 proc poisson: Error running") @@ -56,7 +56,7 @@ def test_qpact_execution_serial(self): def test_qpact_execution_parallel(self): pwd = os.getcwd() os.chdir("../../example/") - command = "mpirun -n 1 python3 -u sfsi_toy_gaussian.py" + command = "mpirun -n 4 python3 -u sfsi_toy_gaussian.py" return_val = os.system(command) os.chdir(pwd) self.assertEqual(return_val, 0, "4 proc qpact: Error running") From d773a214c2da52e00328ceb2201ad11ed9eb122b Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 10:34:23 -0500 Subject: [PATCH 20/50] unit tests update v_1 --- example/poisson_example.py | 53 +++++++++++++++----------------- hippylibX/modeling/PDEProblem.py | 3 ++ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/example/poisson_example.py b/example/poisson_example.py index 5905c708..27b83074 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -43,7 +43,7 @@ def __init__(self, d : float, sigma2 : float): def __call__(self, u : dlx.fem.Function, m: dlx.fem.Function) -> ufl.form.Form: return .5/self.sigma2*ufl.inner(u - self.d, u - self.d)*self.dx -@profile + def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: sep = "\n"+"#"*80+"\n" @@ -110,42 +110,39 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict hpx.parRandom.normal(1.,noise) prior.sample(noise,m0) - # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - - - # if(rank == 0): - # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - # if(comm.size == 1): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: - # pickle.dump(data,f) + if(rank == 0): + data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + if(comm.size == 1): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: + pickle.dump(data,f) - # if(comm.size == 4): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: - # pickle.dump(data,f) + if(comm.size == 4): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: + pickle.dump(data,f) + + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - # eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - - # if(rank == 0): - # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + if(rank == 0): + data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - # if(comm.size == 1): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: - # pickle.dump(data,f) - - # if(comm.size == 4): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: - # pickle.dump(data,f) - + if(comm.size == 1): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: + pickle.dump(data,f) + + if(comm.size == 4): + os.makedirs('../hippylibX/test',exist_ok=True) + with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: + pickle.dump(data,f) # if(rank == 0): diff --git a/hippylibX/modeling/PDEProblem.py b/hippylibX/modeling/PDEProblem.py index 10b93ee8..83fd5cdb 100644 --- a/hippylibX/modeling/PDEProblem.py +++ b/hippylibX/modeling/PDEProblem.py @@ -240,7 +240,9 @@ def setLinearizationPoint(self,x : list, gauss_newton_approx) -> None: if gauss_newton_approx: if(self.Wuu is not None): self.Wuu.destroy() + self.Wuu = None + if(self.Wmu is not None): self.Wmu.destroy() self.Wmu = None @@ -248,6 +250,7 @@ def setLinearizationPoint(self,x : list, gauss_newton_approx) -> None: if(self.Wmm is not None): self.Wmm.destroy() self.Wmm = None + else: if self.Wuu is None: From 2da120844ad3e2ffaf716df9e543bf4e67866c81 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 10:37:55 -0500 Subject: [PATCH 21/50] updated Dockerfile to test memory usage --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index d2c55b48..4384a651 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,4 @@ FROM dolfinx/dolfinx:stable MAINTAINER Venugopal Ranganathan +RUN pip install memory_profiler RUN pip install psutil \ No newline at end of file From 4882861fc57b366e19026a0fdafa3237ffdc8d62 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 21:56:21 -0500 Subject: [PATCH 22/50] updated testing file --- example/poisson_example.py | 91 +++++++--------- example/sfsi_toy_gaussian.py | 108 ++++++++++-------- hippylibX/modeling/modelVerify.py | 45 +------- hippylibX/test/testing_suite_file.py | 157 +++++++++------------------ 4 files changed, 156 insertions(+), 245 deletions(-) diff --git a/example/poisson_example.py b/example/poisson_example.py index 27b83074..a92b1c12 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -9,12 +9,11 @@ import dolfinx.fem.petsc import pickle from memory_profiler import profile - +import time from matplotlib import pyplot as plt sys.path.append( os.environ.get('HIPPYLIBX_BASE_DIR', "../") ) - import hippylibX as hpx def master_print(comm, *args, **kwargs): @@ -116,35 +115,13 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - if(rank == 0): - data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - if(comm.size == 1): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: - pickle.dump(data,f) - - if(comm.size == 4): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: - pickle.dump(data,f) + data_misfit_True = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - if(rank == 0): - data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + data_misfit_False = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - if(comm.size == 1): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: - pickle.dump(data,f) - - if(comm.size == 4): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: - pickle.dump(data,f) - - # if(rank == 0): # print(err_grad,'\n') # print(err_H) @@ -152,41 +129,53 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict # # ####################################### - # prior_mean_copy = prior.generate_parameter(0) - # prior_mean_copy.array[:] = prior_mean.array[:] + prior_mean_copy = prior.generate_parameter(0) + prior_mean_copy.array[:] = prior_mean.array[:] - # x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] - # if rank == 0: - # print( sep, "Find the MAP point", sep) + if rank == 0: + print( sep, "Find the MAP point", sep) - # parameters = hpx.ReducedSpaceNewtonCG_ParameterList() - # parameters["rel_tolerance"] = 1e-6 - # parameters["abs_tolerance"] = 1e-9 - # parameters["max_iter"] = 500 - # parameters["cg_coarse_tolerance"] = 5e-1 - # parameters["globalization"] = "LS" - # parameters["GN_iter"] = 20 - # if rank != 0: - # parameters["print_level"] = -1 + parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + parameters["rel_tolerance"] = 1e-6 + parameters["abs_tolerance"] = 1e-9 + parameters["max_iter"] = 500 + parameters["cg_coarse_tolerance"] = 5e-1 + parameters["globalization"] = "LS" + parameters["GN_iter"] = 20 + if rank != 0: + parameters["print_level"] = -1 - # solver = hpx.ReducedSpaceNewtonCG(model, parameters) + solver = hpx.ReducedSpaceNewtonCG(model, parameters) - # x = solver.solve(x) + x = solver.solve(x) - # if solver.converged: - # master_print(comm, "\nConverged in ", solver.it, " iterations.") - # else: - # master_print(comm, "\nNot Converged") + if solver.converged: + master_print(comm, "\nConverged in ", solver.it, " iterations.") + else: + master_print(comm, "\nNot Converged") + + master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + master_print (comm, "Final cost: ", solver.final_cost) + + optimizer_results = {} + if(solver.termination_reasons[solver.reason] == 'Norm of the gradient less than tolerance'): + optimizer_results['optimizer'] = True + else: + optimizer_results['optimizer'] = False - # master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) - # master_print (comm, "Final gradient norm: ", solver.final_grad_norm) - # master_print (comm, "Final cost: ", solver.final_cost) + + final_results = {"data_misfit_True":data_misfit_True, + "data_misfit_False":data_misfit_False, + "optimizer_results":optimizer_results} - ####################################### + return final_results + ####################################### if __name__ == "__main__": nx = 64 @@ -194,6 +183,6 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict noise_variance = 1e-4 prior_param = {"gamma": 0.1, "delta": 1.} run_inversion(nx, ny, noise_variance, prior_param) - + plt.savefig("poisson_result_FD_Gradient_Hessian_Check") diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index 7b6e473a..b135b457 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -64,7 +64,6 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict comm = MPI.COMM_WORLD rank = comm.rank nproc = comm.size - fname = 'meshes/circle.xdmf' fid = dlx.io.XDMFFile(comm,fname,"r") msh = fid.read_mesh(name='mesh') @@ -129,36 +128,35 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + + # if(rank == 0): + data_misfit_True = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - if(rank == 0): - data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - if(comm.size == 1): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_qpact_1_proc_misfit_True.pickle','wb') as f: - pickle.dump(data,f) - - if(comm.size == 4): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_qpact_4_proc_misfit_True.pickle','wb') as f: - pickle.dump(data,f) + # if(comm.size == 1): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_qpact_1_proc_misfit_True.pickle','wb') as f: + # pickle.dump(data,f) + + # if(comm.size == 4): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_qpact_4_proc_misfit_True.pickle','wb') as f: + # pickle.dump(data,f) eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - if(rank == 0): - data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + # if(rank == 0): + data_misfit_False = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - if(comm.size == 1): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_qpact_1_proc_misfit_False.pickle','wb') as f: - pickle.dump(data,f) - - if(comm.size == 4): - os.makedirs('../hippylibX/test',exist_ok=True) - with open('../hippylibX/test/outputs_qpact_4_proc_misfit_False.pickle','wb') as f: - pickle.dump(data,f) - + # if(comm.size == 1): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_qpact_1_proc_misfit_False.pickle','wb') as f: + # pickle.dump(data,f) + # if(comm.size == 4): + # os.makedirs('../hippylibX/test',exist_ok=True) + # with open('../hippylibX/test/outputs_qpact_4_proc_misfit_False.pickle','wb') as f: + # pickle.dump(data,f) # if(rank == 0): @@ -168,37 +166,52 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict # ####################################### - # prior_mean_copy = prior.generate_parameter(0) - # prior_mean_copy.array[:] = prior_mean.array[:] + prior_mean_copy = prior.generate_parameter(0) + prior_mean_copy.array[:] = prior_mean.array[:] - # x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] - # if rank == 0: - # print( sep, "Find the MAP point", sep) + if rank == 0: + print( sep, "Find the MAP point", sep) - # parameters = hpx.ReducedSpaceNewtonCG_ParameterList() - # parameters["rel_tolerance"] = 1e-6 - # parameters["abs_tolerance"] = 1e-9 - # parameters["max_iter"] = 500 - # parameters["cg_coarse_tolerance"] = 5e-1 - # parameters["globalization"] = "LS" - # parameters["GN_iter"] = 20 - # if rank != 0: - # parameters["print_level"] = -1 + parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + parameters["rel_tolerance"] = 1e-6 + parameters["abs_tolerance"] = 1e-9 + parameters["max_iter"] = 500 + parameters["cg_coarse_tolerance"] = 5e-1 + parameters["globalization"] = "LS" + parameters["GN_iter"] = 20 + if rank != 0: + parameters["print_level"] = -1 - # solver = hpx.ReducedSpaceNewtonCG(model, parameters) + solver = hpx.ReducedSpaceNewtonCG(model, parameters) - # x = solver.solve(x) + x = solver.solve(x) + + if solver.converged: + master_print(comm, "\nConverged in ", solver.it, " iterations.") + else: + master_print(comm, "\nNot Converged") + + master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + master_print (comm, "Final cost: ", solver.final_cost) + + + optimizer_results = {} + if(solver.termination_reasons[solver.reason] == 'Norm of the gradient less than tolerance'): + optimizer_results['optimizer'] = True + else: + optimizer_results['optimizer'] = False + + + final_results = {"data_misfit_True":data_misfit_True, + "data_misfit_False":data_misfit_False, + "optimizer_results":optimizer_results} - # if solver.converged: - # master_print(comm, "\nConverged in ", solver.it, " iterations.") - # else: - # master_print(comm, "\nNot Converged") + return final_results - # master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) - # master_print (comm, "Final gradient norm: ", solver.final_grad_norm) - # master_print (comm, "Final cost: ", solver.final_cost) ####################################### @@ -208,5 +221,6 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict noise_variance = 1e-6 prior_param = {"gamma": 0.05, "delta": 1.} run_inversion(nx, ny, noise_variance, prior_param) + plt.savefig("qpact_result_FD_Gradient_Hessian_Check") diff --git a/hippylibX/modeling/modelVerify.py b/hippylibX/modeling/modelVerify.py index 6a18a411..91c1cfa1 100644 --- a/hippylibX/modeling/modelVerify.py +++ b/hippylibX/modeling/modelVerify.py @@ -93,8 +93,9 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr err_H[i] = err.norm(petsc4py.PETSc.NormType.NORM_INFINITY) err.destroy() - # if verbose: - # modelVerifyPlotErrors(comm, misfit_only,is_quadratic, eps, err_grad, err_H) + if verbose: + modelVerifyPlotErrors(comm, misfit_only,is_quadratic, eps, err_grad, err_H) + #comm and misfit_only are being passed for plotting purposes only- #the title of the plot for saved figures. #has to be removed for the final version. @@ -124,42 +125,6 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr print( "HESSIAN IS NOT SYMMETRIC!!") - #clean-up ops - # if(model.problem.A is not None): - # model.problem.A.destroy() - # model.problem.A = None - - - # if(model.problem.At is not None): - # model.problem.At.destroy() - # model.problem.At = None - - # if(model.problem.C is not None): - # model.problem.C.destroy() - # model.problem.C = None - - - # if(model.problem.Wuu is not None): - # model.problem.Wuu.destroy() - # model.problem.Wuu = None - - - # if(model.problem.Wmu is not None): - # model.problem.Wmu.destroy() - # model.problem.Wmu = None - - - # if(model.problem.Wum is not None): - # model.problem.Wum.destroy() - # model.problem.Wum = None - - - # if(model.problem.Wmm is not None): - # model.problem.Wmm.destroy() - # model.problem.Wmm = None - - - return eps, err_grad, err_H, rel_symm_error @@ -177,7 +142,7 @@ def modelVerifyPlotErrors(comm, misfit_only, is_quadratic : bool, eps : np.ndarr plt.subplot(122) plt.loglog(eps[0], err_H[0], "-ob", [10*eps[0], eps[0], 0.1*eps[0]], [err_H[0],err_H[0],err_H[0]], "-.k") plt.title("FD Hessian Check") - plt.savefig(f"result_with_misfit_{misfit_only}_using_{comm.size}_procs.png") + # plt.savefig(f"result_with_misfit_{misfit_only}_using_{comm.size}_procs.png") else: plt.figure() plt.subplot(121) @@ -186,4 +151,4 @@ def modelVerifyPlotErrors(comm, misfit_only, is_quadratic : bool, eps : np.ndarr plt.subplot(122) plt.loglog(eps, err_H, "-ob", eps, eps*(err_H[0]/eps[0]), "-.k") plt.title("FD Hessian Check") - plt.savefig(f"result_with_misfit_{misfit_only}_using_{comm.size}_procs.png") + # plt.savefig(f"result_with_misfit_{misfit_only}_using_{comm.size}_procs.png") diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 11d08d64..764845f5 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -1,17 +1,17 @@ - import unittest import sys import os import numpy as np -import pickle +from mpi4py import MPI sys.path.append(os.path.abspath('../..')) - import hippylibX as hpx -sys.path.append(os.path.abspath('../../example')) +sys.path.append(os.path.abspath('../../example')) +from example import poisson_example, sfsi_toy_gaussian + def data_parser(data): eps = data["eps"] err_grad = data['err_grad'] @@ -26,125 +26,68 @@ def data_parser(data): return sym_Hessian_value, slope_grad, slope_H - class Testing_Execution(unittest.TestCase): - def test_poisson_execution_serial(self): - pwd = os.getcwd() - os.chdir("../../example/") - command = "mpirun -n 1 python3 -u poisson_example.py" - return_val = os.system(command) - os.chdir(pwd) - self.assertEqual(return_val, 0, "1 proc poisson: Error running") - - def test_poisson_execution_parallel(self): + def test_qpact_execution(self): pwd = os.getcwd() - os.chdir("../../example/") - command = "mpirun -n 4 python3 -u poisson_example.py" - return_val = os.system(command) + nx = 64 + ny = 64 + noise_variance = 1e-6 + prior_param = {"gamma": 0.05, "delta": 1.} + os.chdir("../../example") + out = sfsi_toy_gaussian.run_inversion(nx, ny, noise_variance, prior_param) os.chdir(pwd) - self.assertEqual(return_val, 0, "4 proc poisson: Error running") - - def test_qpact_execution_serial(self): - pwd = os.getcwd() - os.chdir("../../example/") - command = "mpirun -n 1 python3 -u sfsi_toy_gaussian.py" - return_val = os.system(command) - os.chdir(pwd) - self.assertEqual(return_val, 0, "1 proc qpact: Error running") - - def test_qpact_execution_parallel(self): - pwd = os.getcwd() - os.chdir("../../example/") - command = "mpirun -n 4 python3 -u sfsi_toy_gaussian.py" - return_val = os.system(command) - os.chdir(pwd) - self.assertEqual(return_val, 0, "4 proc qpact: Error running") - - def test_qpact_results_serial_misfit_True(self): - - with open('outputs_qpact_1_proc_misfit_True.pickle', 'rb') as f: - data = pickle.load(f) - - sym_Hessian_value, slope_grad, slope_H = data_parser(data) - - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc qpact misfit True: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc qpact misfit True: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc qpact misfit True: FD Hessian check slope is not close to 1") - - def test_qpact_results_serial_misfit_False(self): - #misfit=False - with open('outputs_qpact_1_proc_misfit_False.pickle', 'rb') as f: - data = pickle.load(f) - sym_Hessian_value, slope_grad, slope_H = data_parser(data) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc qpact misfit False: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc qpact misfit False: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc qpact misfit False: FD Hessian check slope is not close to 1") - - def test_qpact_results_parallel_misfit_True(self): - - with open('outputs_qpact_4_proc_misfit_True.pickle', 'rb') as f: - data = pickle.load(f) - - sym_Hessian_value, slope_grad, slope_H = data_parser(data) - - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc qpact misfit True: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc qpact misfit True: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc qpact misfit True: FD Hessian check slope is not close to 1") - - def test_qpact_results_parallel_misfit_False(self): - #misfit=False - with open('outputs_qpact_4_proc_misfit_False.pickle', 'rb') as f: - data = pickle.load(f) - sym_Hessian_value, slope_grad, slope_H = data_parser(data) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc qpact misfit False: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc qpact misfit False: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc qpact misfit False: FD Hessian check slope is not close to 1") + + #convergence of optimizer + self.assertEqual(out['optimizer_results']['optimizer'],True,"Did not converge") + + # misfit = True, slope and symmmetric nature of Hessian + sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_True']) + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "qpact misfit True: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="qpact misfit True: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="qpact misfit True: FD Hessian check slope is not close to 1") + + # misfit = False, slope and symmmetric nature of Hessian + sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_False']) + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "qpact misfit True: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="qpact misfit True: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="qpact misfit True: FD Hessian check slope is not close to 1") + + def test_poisson_execution(self): + nx = 64 + ny = 64 + noise_variance = 1e-4 + prior_param = {"gamma": 0.1, "delta": 1.} + out = poisson_example.run_inversion(nx, ny, noise_variance, prior_param) + + #convergence of optimizer + self.assertEqual(out['optimizer_results']['optimizer'],True,"Did not converge") + + # misfit = True, slope and symmmetric nature of Hessian + sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_True']) + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "poisson misfit True: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="poisson misfit True: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="poisson misfit True: FD Hessian check slope is not close to 1") + # misfit = False, slope and symmmetric nature of Hessian + sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_False']) + self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "poisson misfit True: Symmetric Hessian check value is greater than 1e-10") + self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="poisson misfit True: FD Gradient check slope is not close to 1") + self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="poisson misfit True: FD Hessian check slope is not close to 1") - def test_poisson_results_serial_misfit_True(self): - with open('outputs_poisson_1_proc_misfit_True.pickle', 'rb') as f: - data = pickle.load(f) + +if __name__ == "__main__": + unittest.main() - sym_Hessian_value, slope_grad, slope_H = data_parser(data) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc poisson misfit True: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc poisson misfit True: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc poisson misfit True: FD Hessian check slope is not close to 1") - def test_poisson_results_serial_misfit_False(self): - #misfit=False - with open('outputs_poisson_1_proc_misfit_False.pickle', 'rb') as f: - data = pickle.load(f) - sym_Hessian_value, slope_grad, slope_H = data_parser(data) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "1 proc poisson misfit False: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="1 proc poisson misfit False: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="1 proc poisson misfit False: FD Hessian check slope is not close to 1") - def test_poisson_results_parallel_misfit_True(self): - with open('outputs_poisson_4_proc_misfit_True.pickle', 'rb') as f: - data = pickle.load(f) - sym_Hessian_value, slope_grad, slope_H = data_parser(data) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc poisson misfit True: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc poisson misfit True: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc poisson misfit True: FD Hessian check slope is not close to 1") - def test_qpact_results_parallel_misfit_False(self): - #misfit=False - with open('outputs_poisson_4_proc_misfit_False.pickle', 'rb') as f: - data = pickle.load(f) - sym_Hessian_value, slope_grad, slope_H = data_parser(data) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "4 proc poisson misfit False: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="4 proc poisson misfit False: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="4 proc poisson misfit False: FD Hessian check slope is not close to 1") - -if __name__ == "__main__": - unittest.main() From 6062ee58e08fcd735d7cfdd7d0a3556db236ad60 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 22:25:50 -0500 Subject: [PATCH 23/50] CI attempt --- .github/workflows/CI_testing.yml | 22 +++++++++++++++------- hippylibX/test/testing_suite_file.py | 22 ++++++++++++++++++++-- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 0e9ad2a5..194b5593 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -17,12 +17,20 @@ jobs: steps: - name: run code uses: actions/checkout@v2 - - name: run the file - run: cd ./example && mpirun -n 1 python3 -u sfsi_toy_gaussian.py - - name: change directory and validate + - name: run serial check run: | - cd ./example/testing_folder && - output=$(python3 test_script.py) && - if [[ "$output" == "1" ]]; then + cd ./hippylibX/test && + output=$(mpirun -n 1 python3 -u testing_suite_file.py) && + if [[ "$output" == "1" ]]; then exit 1 - fi \ No newline at end of file + fi + + - name: run parallel check + run: | + cd ./hippylibX/test && + output=$(mpirun -n 4 python3 -u testing_suite_file.py) && + if [[ "$output" == "1" ]]; then + exit 1 + fi + + diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 764845f5..070e1438 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -6,10 +6,11 @@ sys.path.append(os.path.abspath('../..')) + import hippylibX as hpx +sys.path.append(os.path.abspath('../../example')) -sys.path.append(os.path.abspath('../../example')) from example import poisson_example, sfsi_toy_gaussian def data_parser(data): @@ -26,6 +27,18 @@ def data_parser(data): return sym_Hessian_value, slope_grad, slope_H + +class Test_runner: + def __init__(self): + self.result = unittest.TestResult() + + def run_tests(self): + test_suite = unittest.TestLoader().loadTestsFromTestCase(Testing_Execution) + test_suite.run(self.result) + + return 1 if self.result.wasSuccessful() else 0 + + class Testing_Execution(unittest.TestCase): def test_qpact_execution(self): @@ -71,14 +84,19 @@ def test_poisson_execution(self): # misfit = False, slope and symmmetric nature of Hessian sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_False']) + print(slope_grad,slope_H) self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "poisson misfit True: Symmetric Hessian check value is greater than 1e-10") self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="poisson misfit True: FD Gradient check slope is not close to 1") self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="poisson misfit True: FD Hessian check slope is not close to 1") + pass if __name__ == "__main__": - unittest.main() + test_suite = unittest.defaultTestLoader.discover('.','testing_suite_file.py') + test_runner = unittest.TextTestRunner(resultclass=unittest.TextTestResult) + result = test_runner.run(test_suite) + sys.exit(not result.wasSuccessful()) From 48857d745fd2fbb51222df3e0fb3159546b82ada Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 22:28:57 -0500 Subject: [PATCH 24/50] removed mem_prof import --- example/poisson_example.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/example/poisson_example.py b/example/poisson_example.py index a92b1c12..31224092 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -8,7 +8,6 @@ import os import dolfinx.fem.petsc import pickle -from memory_profiler import profile import time from matplotlib import pyplot as plt @@ -166,7 +165,7 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict else: optimizer_results['optimizer'] = False - + final_results = {"data_misfit_True":data_misfit_True, "data_misfit_False":data_misfit_False, "optimizer_results":optimizer_results} From 29c38f28f4cd69a5b6415b495cb76e75b152ebe5 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 22:54:11 -0500 Subject: [PATCH 25/50] update test file --- .github/workflows/CI_testing.yml | 4 ++-- hippylibX/test/testing_suite_file.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 194b5593..d71aa2ca 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -20,7 +20,7 @@ jobs: - name: run serial check run: | cd ./hippylibX/test && - output=$(mpirun -n 1 python3 -u testing_suite_file.py) && + output=$(mpirun -n 1 python3 testing_suite_file.py) && if [[ "$output" == "1" ]]; then exit 1 fi @@ -28,7 +28,7 @@ jobs: - name: run parallel check run: | cd ./hippylibX/test && - output=$(mpirun -n 4 python3 -u testing_suite_file.py) && + output=$(mpirun -n 4 python3 testing_suite_file.py) && if [[ "$output" == "1" ]]; then exit 1 fi diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 070e1438..82f5f0cd 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -84,7 +84,6 @@ def test_poisson_execution(self): # misfit = False, slope and symmmetric nature of Hessian sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_False']) - print(slope_grad,slope_H) self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "poisson misfit True: Symmetric Hessian check value is greater than 1e-10") self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="poisson misfit True: FD Gradient check slope is not close to 1") self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="poisson misfit True: FD Hessian check slope is not close to 1") From 94ab33c02b0ce7b2beeaa0541e245e094220720b Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 23:02:30 -0500 Subject: [PATCH 26/50] update CI from 4 proc to 2 proc --- .github/workflows/CI_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index d71aa2ca..3563acf7 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -28,7 +28,7 @@ jobs: - name: run parallel check run: | cd ./hippylibX/test && - output=$(mpirun -n 4 python3 testing_suite_file.py) && + output=$(mpirun -n 2 python3 testing_suite_file.py) && if [[ "$output" == "1" ]]; then exit 1 fi From f80fc13a3bc4866480fe27cc0d52660e5bcee8a9 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 23:18:19 -0500 Subject: [PATCH 27/50] testing if CI fails --- example/poisson_example.py | 12 +--------- example/sfsi_toy_gaussian.py | 35 +--------------------------- hippylibX/modeling/modelVerify.py | 11 +++------ hippylibX/test/testing_suite_file.py | 20 +++++++++++++--- 4 files changed, 22 insertions(+), 56 deletions(-) diff --git a/example/poisson_example.py b/example/poisson_example.py index 31224092..ee528111 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -7,8 +7,6 @@ import sys import os import dolfinx.fem.petsc -import pickle -import time from matplotlib import pyplot as plt @@ -108,9 +106,6 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict hpx.parRandom.normal(1.,noise) prior.sample(noise,m0) - # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) @@ -120,12 +115,7 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) data_misfit_False = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - - # if(rank == 0): - # print(err_grad,'\n') - # print(err_H) - # plt.show() - + # # ####################################### prior_mean_copy = prior.generate_parameter(0) diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index b135b457..49550c90 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -8,10 +8,6 @@ import os import dolfinx.fem.petsc -# for validation purposes only - to write out modelVerify results -import pickle - - from matplotlib import pyplot as plt sys.path.append( os.environ.get('HIPPYLIBX_BASE_DIR', "../") ) @@ -129,41 +125,12 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - # if(rank == 0): data_misfit_True = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - # if(comm.size == 1): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_qpact_1_proc_misfit_True.pickle','wb') as f: - # pickle.dump(data,f) - - # if(comm.size == 4): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_qpact_4_proc_misfit_True.pickle','wb') as f: - # pickle.dump(data,f) - - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - # if(rank == 0): data_misfit_False = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - - # if(comm.size == 1): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_qpact_1_proc_misfit_False.pickle','wb') as f: - # pickle.dump(data,f) - - # if(comm.size == 4): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_qpact_4_proc_misfit_False.pickle','wb') as f: - # pickle.dump(data,f) - - - # if(rank == 0): - # print(err_grad,'\n') - # print(err_H) - # plt.show() - + # ####################################### prior_mean_copy = prior.generate_parameter(0) diff --git a/hippylibX/modeling/modelVerify.py b/hippylibX/modeling/modelVerify.py index 91c1cfa1..27de2fcb 100644 --- a/hippylibX/modeling/modelVerify.py +++ b/hippylibX/modeling/modelVerify.py @@ -94,12 +94,9 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr err.destroy() if verbose: - modelVerifyPlotErrors(comm, misfit_only,is_quadratic, eps, err_grad, err_H) + modelVerifyPlotErrors(is_quadratic, eps, err_grad, err_H) - #comm and misfit_only are being passed for plotting purposes only- - #the title of the plot for saved figures. - #has to be removed for the final version. - + temp_petsc_vec_grad_x.destroy() xx = model.generate_vector(PARAMETER) @@ -128,7 +125,7 @@ def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadr return eps, err_grad, err_H, rel_symm_error -def modelVerifyPlotErrors(comm, misfit_only, is_quadratic : bool, eps : np.ndarray, err_grad : np.ndarray, err_H : np.ndarray) -> None: +def modelVerifyPlotErrors(is_quadratic : bool, eps : np.ndarray, err_grad : np.ndarray, err_H : np.ndarray) -> None: try: import matplotlib.pyplot as plt except: @@ -142,7 +139,6 @@ def modelVerifyPlotErrors(comm, misfit_only, is_quadratic : bool, eps : np.ndarr plt.subplot(122) plt.loglog(eps[0], err_H[0], "-ob", [10*eps[0], eps[0], 0.1*eps[0]], [err_H[0],err_H[0],err_H[0]], "-.k") plt.title("FD Hessian Check") - # plt.savefig(f"result_with_misfit_{misfit_only}_using_{comm.size}_procs.png") else: plt.figure() plt.subplot(121) @@ -151,4 +147,3 @@ def modelVerifyPlotErrors(comm, misfit_only, is_quadratic : bool, eps : np.ndarr plt.subplot(122) plt.loglog(eps, err_H, "-ob", eps, eps*(err_H[0]/eps[0]), "-.k") plt.title("FD Hessian Check") - # plt.savefig(f"result_with_misfit_{misfit_only}_using_{comm.size}_procs.png") diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 82f5f0cd..9d7274ae 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -13,21 +13,35 @@ from example import poisson_example, sfsi_toy_gaussian +# def data_parser(data): +# eps = data["eps"] +# err_grad = data['err_grad'] +# err_H = data['err_H'] +# sym_Hessian_value = data['sym_Hessian_value'] + +# slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) +# slope_grad = slope_grad_coeffs[0] + +# slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) +# slope_H = slope_H_coeffs[0] + +# return sym_Hessian_value, slope_grad, slope_H + + def data_parser(data): eps = data["eps"] err_grad = data['err_grad'] err_H = data['err_H'] sym_Hessian_value = data['sym_Hessian_value'] - slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) + slope_grad_coeffs = np.polyfit(np.log(eps[10:]), np.log(err_grad[10:]), 1) slope_grad = slope_grad_coeffs[0] - slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) + slope_H_coeffs = np.polyfit(np.log(eps[10:]), np.log(err_H[10:]), 1) slope_H = slope_H_coeffs[0] return sym_Hessian_value, slope_grad, slope_H - class Test_runner: def __init__(self): self.result = unittest.TestResult() From cfc422ae30be31fc7b18100aab7fc9d736d8dc4d Mon Sep 17 00:00:00 2001 From: V-Rang Date: Fri, 15 Mar 2024 23:25:44 -0500 Subject: [PATCH 28/50] testing suite working as expected --- hippylibX/test/testing_suite_file.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 9d7274ae..82f5f0cd 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -13,35 +13,21 @@ from example import poisson_example, sfsi_toy_gaussian -# def data_parser(data): -# eps = data["eps"] -# err_grad = data['err_grad'] -# err_H = data['err_H'] -# sym_Hessian_value = data['sym_Hessian_value'] - -# slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) -# slope_grad = slope_grad_coeffs[0] - -# slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) -# slope_H = slope_H_coeffs[0] - -# return sym_Hessian_value, slope_grad, slope_H - - def data_parser(data): eps = data["eps"] err_grad = data['err_grad'] err_H = data['err_H'] sym_Hessian_value = data['sym_Hessian_value'] - slope_grad_coeffs = np.polyfit(np.log(eps[10:]), np.log(err_grad[10:]), 1) + slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) slope_grad = slope_grad_coeffs[0] - slope_H_coeffs = np.polyfit(np.log(eps[10:]), np.log(err_H[10:]), 1) + slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) slope_H = slope_H_coeffs[0] return sym_Hessian_value, slope_grad, slope_H + class Test_runner: def __init__(self): self.result = unittest.TestResult() From 086a453585a25afd6a430129d1d1ede2faad8394 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 08:23:48 -0500 Subject: [PATCH 29/50] clean up --- example/poisson_example.py | 8 +++--- example/sfsi_toy_gaussian.py | 7 +++--- hippylibX/algorithms/NewtonCG.py | 1 - hippylibX/algorithms/linSolvers.py | 2 +- hippylibX/modeling/PDEProblem.py | 2 -- hippylibX/modeling/Regularization.py | 37 +++------------------------- hippylibX/modeling/misfit.py | 6 ++--- hippylibX/modeling/modelVerify.py | 2 +- hippylibX/modeling/prior.py | 36 +++------------------------ 9 files changed, 18 insertions(+), 83 deletions(-) diff --git a/example/poisson_example.py b/example/poisson_example.py index ee528111..64a3a248 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -92,7 +92,7 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict d.x.scatter_forward() misfit_form = PoissonMisfitForm(d,noise_variance) - misfit = hpx.NonGaussianContinuousMisfit(msh, Vh, misfit_form) + misfit = hpx.NonGaussianContinuousMisfit(Vh, misfit_form) prior_mean = dlx.fem.Function(Vh_m) prior_mean.x.array[:] = 0.01 @@ -107,12 +107,12 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict prior.sample(noise,m0) - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) data_misfit_True = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) data_misfit_False = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} @@ -173,5 +173,5 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict prior_param = {"gamma": 0.1, "delta": 1.} run_inversion(nx, ny, noise_variance, prior_param) plt.savefig("poisson_result_FD_Gradient_Hessian_Check") - + plt.show() diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index 49550c90..d083e572 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -108,7 +108,7 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict d.x.scatter_forward() misfit_form = PACTMisfitForm(d, noise_variance) - misfit = hpx.NonGaussianContinuousMisfit(msh, Vh, misfit_form) + misfit = hpx.NonGaussianContinuousMisfit(Vh, misfit_form) prior_mean = dlx.fem.Function(Vh_m) prior_mean.x.array[:] = 0.01 @@ -123,11 +123,11 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict prior.sample(noise,m0) - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) data_misfit_True = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) data_misfit_False = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} @@ -189,5 +189,6 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict prior_param = {"gamma": 0.05, "delta": 1.} run_inversion(nx, ny, noise_variance, prior_param) plt.savefig("qpact_result_FD_Gradient_Hessian_Check") + plt.show() diff --git a/hippylibX/algorithms/NewtonCG.py b/hippylibX/algorithms/NewtonCG.py index 2e3b2cdf..d05bd3c6 100644 --- a/hippylibX/algorithms/NewtonCG.py +++ b/hippylibX/algorithms/NewtonCG.py @@ -200,7 +200,6 @@ def _solve_ls(self,x): gradnorm_ini = gradnorm tol = max(abs_tol, gradnorm_ini*rel_tol) - # print(tol) # check if solution is reached if (gradnorm < tol) and (self.it > 0): diff --git a/hippylibX/algorithms/linSolvers.py b/hippylibX/algorithms/linSolvers.py index 099c7754..4a1b2930 100644 --- a/hippylibX/algorithms/linSolvers.py +++ b/hippylibX/algorithms/linSolvers.py @@ -2,7 +2,7 @@ #I don't use either of the following 2 functions for now, -# currently just using this in the _createLUSolver method in +# currently just using the following: in the _createLUSolver method in # the PDEVariationalProblem class in modeling/PDEProblem.py file # def _createLUSolver(self): diff --git a/hippylibX/modeling/PDEProblem.py b/hippylibX/modeling/PDEProblem.py index 83fd5cdb..2391b546 100644 --- a/hippylibX/modeling/PDEProblem.py +++ b/hippylibX/modeling/PDEProblem.py @@ -275,8 +275,6 @@ def setLinearizationPoint(self,x : list, gauss_newton_approx) -> None: self.Wmu = self.Wum.copy() self.Wmu.transpose() - # print(self.Wum.isTranspose(self.Wmu)) #True - if self.Wmm is None: self.Wmm = dlx.fem.petsc.create_matrix(dlx.fem.form(ufl.derivative(g_form[PARAMETER],x_fun[PARAMETER],x_fun_trial[PARAMETER]))) diff --git a/hippylibX/modeling/Regularization.py b/hippylibX/modeling/Regularization.py index 67dd239d..8dda1263 100644 --- a/hippylibX/modeling/Regularization.py +++ b/hippylibX/modeling/Regularization.py @@ -8,69 +8,38 @@ import numpy as np class VariationalRegularization: - def __init__(self, mesh, Vh, functional_handler, isQuadratic=False): + def __init__(self, Vh, functional_handler, isQuadratic=False): self.Vh = Vh #Function space of the parameter. self.functional_handler = functional_handler #a function or a functor that takes as input m (as dl.Function) and evaluate the regularization functional self.isQuadratic = isQuadratic #Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian dependes on m - self.mesh = mesh #to allreduce over the entire mesh in the cost function self.xfun = dlx.fem.Function(self.Vh) - - # def cost(self, m): - # 1. Cast the petsc4py vector m to a dlx.Function mfun - # 2. Compute the cost by calling assemble on self.functional_handler(mfun) - # 3. Return the value of the cost (Make sure to call a AllReduce for parallel computations - - def cost(self,m): - # mfun = vector2Function(m,self.Vh) + def cost(self,m): updateFromVector(self.xfun, m) mfun = self.xfun - loc_cost = self.functional_handler(mfun) glb_cost_proc = dlx.fem.assemble_scalar(dlx.fem.form(loc_cost)) - return self.mesh.comm.allreduce(glb_cost_proc, op=MPI.SUM ) + return self.Vh[STATE].mesh.comm.allreduce(glb_cost_proc, op=MPI.SUM ) - - # def grad(self, m, out): - # 1. Cast the petsc4py vector m to a dlx.Function mfun - # 2. call symbolic differentation of self.functional_handler(mfun) wrt mfun - # 3. call assemble, update ghosts, and store the result in out def grad(self, x : dlx.la.Vector, out: dlx.la.Vector) -> None: - # mfun = vector2Function(x,self.Vh) updateFromVector(self.xfun, x) mfun = self.xfun - - L = dlx.fem.form(ufl.derivative(self.functional_handler(mfun),mfun,ufl.TestFunction(self.Vh))) - out.array[:] = 0. tmp_out = dlx.la.create_petsc_vector_wrap(out) tmp_out.ghostUpdate(addv=petsc4py.PETSc.InsertMode.ADD_VALUES, mode=petsc4py.PETSc.ScatterMode.REVERSE) tmp_out.destroy() - # def setLinearizationPoint(self, m): - # 1. Cast the petsc4py vector m to a dlx.Function mfun - # 2. call symbolic differentiation (twice) to get the second variation of self.functional_handler(mfun) wrt mfun - # 3. assemble the Hessian operator (it's a sparse matrix!) in the attribute self.R - # 4. set up a linearsolver self.Rsolver that uses CG as Krylov method, gamg as preconditioner and self.R as operator - def setLinearizationPoint(self, m, rel_tol=1e-12, max_iter=1000): - # mfun = vector2Function(m,self.Vh) updateFromVector(self.xfun, m) mfun = self.xfun - L = ufl.derivative(ufl.derivative(self.functional_handler(mfun),mfun), mfun) self.R = dlx.fem.petsc.assemble_matrix(dlx.fem.form(L)) self.R.assemble() - - #for Rsolver: - #have to call _BilaplacianRsolver(self.Asolver,self.M) - #so have to construct self.Asolver, self.M first - self.Rsolver = petsc4py.PETSc.KSP().create() self.Rsolver.getPC().setType(petsc4py.PETSc.PC.Type.GAMG) self.Rsolver.setType(petsc4py.PETSc.KSP.Type.CG) diff --git a/hippylibX/modeling/misfit.py b/hippylibX/modeling/misfit.py index ef014b17..c9e5b1da 100644 --- a/hippylibX/modeling/misfit.py +++ b/hippylibX/modeling/misfit.py @@ -6,9 +6,7 @@ import numpy as np class NonGaussianContinuousMisfit(object): - def __init__(self, mesh : dlx.mesh.Mesh, Vh : list, form): - #mesh needed for comm.allreduce in cost function - self.mesh = mesh + def __init__(self,Vh : list, form): self.Vh = Vh self.form = form @@ -28,7 +26,7 @@ def cost(self,x : list) -> float: loc_cost = self.form(u_fun,m_fun) glb_cost_proc = dlx.fem.assemble_scalar(dlx.fem.form(loc_cost)) - return self.mesh.comm.allreduce(glb_cost_proc, op=MPI.SUM ) + return self.Vh[hpx.STATE].mesh.comm.allreduce(glb_cost_proc, op=MPI.SUM ) def grad(self, i : int, x : list, out: dlx.la.Vector) -> dlx.la.Vector: diff --git a/hippylibX/modeling/modelVerify.py b/hippylibX/modeling/modelVerify.py index 27de2fcb..fcdf3a1e 100644 --- a/hippylibX/modeling/modelVerify.py +++ b/hippylibX/modeling/modelVerify.py @@ -11,7 +11,7 @@ from ..utils import vector2Function from ..algorithms import linalg -def modelVerify(comm : mpi4py.MPI.Intracomm, model, m0 : dlx.la.Vector, is_quadratic = False, misfit_only=False, verbose = True, eps = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]: +def modelVerify(model, m0 : dlx.la.Vector, is_quadratic = False, misfit_only=False, verbose = True, eps = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ Verify the reduced Gradient and the Hessian of a model. diff --git a/hippylibX/modeling/prior.py b/hippylibX/modeling/prior.py index e54951aa..c877b354 100644 --- a/hippylibX/modeling/prior.py +++ b/hippylibX/modeling/prior.py @@ -126,17 +126,13 @@ def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean self.M = dlx.fem.petsc.assemble_matrix(dlx.fem.form(varfM)) self.M.assemble() - - # self.Msolver = petsc4py.PETSc.KSP().create() - # self.Msolver.getPC().setType(petsc4py.PETSc.PC.Type.JACOBI) - # self.Msolver.setType(petsc4py.PETSc.KSP.Type.CG) - + self.Msolver = self._createsolver() if(self.petsc_options['pc_type'] == 'hypre'): pc = self.Msolver.getPC() pc.setHYPREType('boomeramg') - self.Msolver.setIterationNumber(max_iter) #these values should be supplied as arguments. + self.Msolver.setIterationNumber(max_iter) self.Msolver.setTolerances(rtol=rel_tol) self.Msolver.setErrorIfNotConverged(True) self.Msolver.setInitialGuessNonzero(False) @@ -144,15 +140,12 @@ def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean self.A = dlx.fem.petsc.assemble_matrix(dlx.fem.form(sqrt_precision_varf_handler(trial, test) )) self.A.assemble() - # self.Asolver = petsc4py.PETSc.KSP().create() - # self.Asolver.getPC().setType(petsc4py.PETSc.PC.Type.GAMG) - # self.Asolver.setType(petsc4py.PETSc.KSP.Type.CG) self.Asolver = self._createsolver() if(self.petsc_options['pc_type'] == 'hypre'): pc = self.Asolver.getPC() pc.setHYPREType('boomeramg') - self.Asolver.setIterationNumber(max_iter) #these values should be supplied as arguments. + self.Asolver.setIterationNumber(max_iter) self.Asolver.setTolerances(rtol=rel_tol) self.Asolver.setErrorIfNotConverged(True) self.Asolver.setInitialGuessNonzero(False) @@ -198,27 +191,6 @@ def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean if self.mean is None: self.mean = self.init_vector(0) - #replaced function init_vector with generate_parameter - @unused_function - def init_vector(self, dim : int) -> dlx.la.Vector: - - """ - Inizialize a vector :code:`x` to be compatible with the range/domain of :math:`R`. - If :code:`dim == "noise"` inizialize :code:`x` to be compatible with the size of - white noise used for sampling. - """ - - if dim == "noise": - return dlx.la.vector( self.sqrtM_function_space.dofmap.index_map ) - - else: - if(dim == 0): - return dlx.la.vector( self.Vh.dofmap.index_map) - - else: - return dlx.la.vector( self.Vh.dofmap.index_map ) - - #Modified version of above init_vector def generate_parameter(self, dim : int) -> dlx.la.Vector: """ Inizialize a vector :code:`x` to be compatible with the range/domain of :math:`R`. @@ -230,7 +202,6 @@ def generate_parameter(self, dim : int) -> dlx.la.Vector: else: return dlx.la.vector( self.Vh.dofmap.index_map ) - #need to construct sqrtM and Asolver from the prior in hippylib def sample(self, noise : dlx.la.Vector, s : dlx.la.Vector, add_mean=True) -> None: """ @@ -240,7 +211,6 @@ def sample(self, noise : dlx.la.Vector, s : dlx.la.Vector, add_mean=True) -> Non """ temp_petsc_vec_noise = dlx.la.create_petsc_vector_wrap(noise) - # rhs = self.sqrtM*temp_petsc_vec_noise rhs = self.sqrtM.createVecLeft() self.sqrtM.mult(temp_petsc_vec_noise,rhs) temp_petsc_vec_noise.destroy() From 666045846a4eee30a24a5ac366fbcfd15a31636a Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 12:28:11 -0500 Subject: [PATCH 30/50] testing CI v_1 --- .github/workflows/CI_testing.yml | 37 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 3563acf7..796f75cd 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -3,10 +3,12 @@ on: push: branches: - numpy_debug_v2 + pull_request: defaults: run: shell: bash -l {0} + jobs: style: runs-on: ubuntu-22.04 @@ -14,23 +16,28 @@ jobs: image: dolfinx/dolfinx:stable options: --user 1001 --privileged name: CI test + steps: - - name: run code + - name: Checkout code uses: actions/checkout@v2 + - name: run serial check - run: | - cd ./hippylibX/test && - output=$(mpirun -n 1 python3 testing_suite_file.py) && - if [[ "$output" == "1" ]]; then - exit 1 - fi - - - name: run parallel check - run: | - cd ./hippylibX/test && - output=$(mpirun -n 2 python3 testing_suite_file.py) && - if [[ "$output" == "1" ]]; then - exit 1 - fi + run: mpirun -n 1 python3 testing_suite_file.py + + # - name: run serial check + # run: | + # cd ./hippylibX/test && + # output=$(mpirun -n 1 python3 testing_suite_file.py) && + # if [[ "$output" == "1" ]]; then + # exit 1 + # fi + + # - name: run parallel check + # run: | + # cd ./hippylibX/test && + # output=$(mpirun -n 2 python3 testing_suite_file.py) && + # if [[ "$output" == "1" ]]; then + # exit 1 + # fi From a6743bdf4f5282d158cf36712d3556e9d7b1f54f Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 12:30:07 -0500 Subject: [PATCH 31/50] testing CI v_1 --- .github/workflows/CI_testing.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 796f75cd..440f8fb5 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -22,7 +22,9 @@ jobs: uses: actions/checkout@v2 - name: run serial check - run: mpirun -n 1 python3 testing_suite_file.py + run: | + cd ./hippylibx/test && + mpirun -n 1 python3 testing_suite_file.py # - name: run serial check # run: | From 7637683205cbaa2428b75bc0209427b13be41f97 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 12:33:05 -0500 Subject: [PATCH 32/50] testing CI v_1 --- .github/workflows/CI_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 440f8fb5..24670973 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -23,7 +23,7 @@ jobs: - name: run serial check run: | - cd ./hippylibx/test && + cd ./hippylibX/test && mpirun -n 1 python3 testing_suite_file.py # - name: run serial check From dd564d747da9d35223cfa63131070f5d0e927a57 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 12:37:47 -0500 Subject: [PATCH 33/50] testing CI - expected to fail --- hippylibX/test/testing_suite_file.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 82f5f0cd..b9cdb2ab 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -13,16 +13,31 @@ from example import poisson_example, sfsi_toy_gaussian +# def data_parser(data): +# eps = data["eps"] +# err_grad = data['err_grad'] +# err_H = data['err_H'] +# sym_Hessian_value = data['sym_Hessian_value'] + +# slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) +# slope_grad = slope_grad_coeffs[0] + +# slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) +# slope_H = slope_H_coeffs[0] + +# return sym_Hessian_value, slope_grad, slope_H + + def data_parser(data): eps = data["eps"] err_grad = data['err_grad'] err_H = data['err_H'] sym_Hessian_value = data['sym_Hessian_value'] - slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) + slope_grad_coeffs = np.polyfit(np.log(eps[10:]), np.log(err_grad[10:]), 1) slope_grad = slope_grad_coeffs[0] - slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) + slope_H_coeffs = np.polyfit(np.log(eps[10:]), np.log(err_H[10:]), 1) slope_H = slope_H_coeffs[0] return sym_Hessian_value, slope_grad, slope_H @@ -88,8 +103,6 @@ def test_poisson_execution(self): self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="poisson misfit True: FD Gradient check slope is not close to 1") self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="poisson misfit True: FD Hessian check slope is not close to 1") - pass - if __name__ == "__main__": test_suite = unittest.defaultTestLoader.discover('.','testing_suite_file.py') From c5a8b8277694397b276b5eb1f9efde5cb51dd14a Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 12:50:07 -0500 Subject: [PATCH 34/50] checking CI after minor changes to return values from modelVerify --- .github/workflows/CI_testing.yml | 22 ++++++---------------- example/poisson_example.py | 16 +++++++--------- example/sfsi_toy_gaussian.py | 17 ++++++++--------- hippylibX/modeling/modelVerify.py | 2 +- hippylibX/test/testing_suite_file.py | 21 ++++----------------- 5 files changed, 26 insertions(+), 52 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index 24670973..a17ad40f 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -2,7 +2,8 @@ name: CI on: push: branches: - - numpy_debug_v2 + - numpy_debug_v2 + #make it main once it is working as expected on numpy_debug_v2 branch. pull_request: defaults: @@ -26,20 +27,9 @@ jobs: cd ./hippylibX/test && mpirun -n 1 python3 testing_suite_file.py - # - name: run serial check - # run: | - # cd ./hippylibX/test && - # output=$(mpirun -n 1 python3 testing_suite_file.py) && - # if [[ "$output" == "1" ]]; then - # exit 1 - # fi - - # - name: run parallel check - # run: | - # cd ./hippylibX/test && - # output=$(mpirun -n 2 python3 testing_suite_file.py) && - # if [[ "$output" == "1" ]]; then - # exit 1 - # fi + - name: run parallel check + run: | + cd ./hippylibX/test && + mpirun -n 2 python3 testing_suite_file.py diff --git a/example/poisson_example.py b/example/poisson_example.py index 64a3a248..29fd190d 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -107,14 +107,9 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict prior.sample(noise,m0) - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + data_misfit_True = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - data_misfit_True = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - - - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - - data_misfit_False = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} + data_misfit_False = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) # # ####################################### @@ -172,6 +167,9 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict noise_variance = 1e-4 prior_param = {"gamma": 0.1, "delta": 1.} run_inversion(nx, ny, noise_variance, prior_param) - plt.savefig("poisson_result_FD_Gradient_Hessian_Check") - plt.show() + comm = MPI.COMM_WORLD + + if(comm.rank == 0): + plt.savefig("poisson_result_FD_Gradient_Hessian_Check") + plt.show() diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index d083e572..8ec12554 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -122,15 +122,11 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict hpx.parRandom.normal(1.,noise) prior.sample(noise,m0) + data_misfit_True = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + + data_misfit_False = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - data_misfit_True = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - - data_misfit_False = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - # ####################################### prior_mean_copy = prior.generate_parameter(0) @@ -188,7 +184,10 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict noise_variance = 1e-6 prior_param = {"gamma": 0.05, "delta": 1.} run_inversion(nx, ny, noise_variance, prior_param) - plt.savefig("qpact_result_FD_Gradient_Hessian_Check") - plt.show() + + comm = MPI.COMM_WORLD + if(comm.rank == 0): + plt.savefig("qpact_result_FD_Gradient_Hessian_Check") + plt.show() diff --git a/hippylibX/modeling/modelVerify.py b/hippylibX/modeling/modelVerify.py index fcdf3a1e..01784146 100644 --- a/hippylibX/modeling/modelVerify.py +++ b/hippylibX/modeling/modelVerify.py @@ -122,7 +122,7 @@ def modelVerify(model, m0 : dlx.la.Vector, is_quadratic = False, misfit_only=Fal print( "HESSIAN IS NOT SYMMETRIC!!") - return eps, err_grad, err_H, rel_symm_error + return {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} def modelVerifyPlotErrors(is_quadratic : bool, eps : np.ndarray, err_grad : np.ndarray, err_H : np.ndarray) -> None: diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index b9cdb2ab..7dce9cf6 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -13,36 +13,23 @@ from example import poisson_example, sfsi_toy_gaussian -# def data_parser(data): -# eps = data["eps"] -# err_grad = data['err_grad'] -# err_H = data['err_H'] -# sym_Hessian_value = data['sym_Hessian_value'] - -# slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) -# slope_grad = slope_grad_coeffs[0] - -# slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) -# slope_H = slope_H_coeffs[0] - -# return sym_Hessian_value, slope_grad, slope_H - - def data_parser(data): eps = data["eps"] err_grad = data['err_grad'] err_H = data['err_H'] sym_Hessian_value = data['sym_Hessian_value'] - slope_grad_coeffs = np.polyfit(np.log(eps[10:]), np.log(err_grad[10:]), 1) + slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) slope_grad = slope_grad_coeffs[0] - slope_H_coeffs = np.polyfit(np.log(eps[10:]), np.log(err_H[10:]), 1) + slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) slope_H = slope_H_coeffs[0] return sym_Hessian_value, slope_grad, slope_H + + class Test_runner: def __init__(self): self.result = unittest.TestResult() From 0c5567e305c1fbfc335c92ed9b3b380de5c2055a Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 13:09:33 -0500 Subject: [PATCH 35/50] changes to testing suite --- example/sfsi_toy_gaussian.py | 8 +++++--- hippylibX/test/testing_suite_file.py | 7 +++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index 8ec12554..0cb090b4 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -54,13 +54,14 @@ def __init__(self, d : float, sigma2 : float): def __call__(self,u : dlx.fem.Function, m : dlx.fem.Function) -> ufl.form.Form: return .5/self.sigma2*ufl.inner(u*ufl.exp(m) -self.d, u*ufl.exp(m) -self.d)*self.dx -def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: +def run_inversion(mesh_path: str, nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: sep = "\n"+"#"*80+"\n" comm = MPI.COMM_WORLD rank = comm.rank nproc = comm.size - fname = 'meshes/circle.xdmf' + # fname = 'meshes/circle.xdmf' + fname = f'{mesh_path}/circle.xdmf' fid = dlx.io.XDMFFile(comm,fname,"r") msh = fid.read_mesh(name='mesh') @@ -183,7 +184,8 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict ny = 64 noise_variance = 1e-6 prior_param = {"gamma": 0.05, "delta": 1.} - run_inversion(nx, ny, noise_variance, prior_param) + mesh_path = 'meshes' + run_inversion(mesh_path, nx, ny, noise_variance, prior_param) comm = MPI.COMM_WORLD if(comm.rank == 0): diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 7dce9cf6..4127717f 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -44,14 +44,13 @@ def run_tests(self): class Testing_Execution(unittest.TestCase): def test_qpact_execution(self): - pwd = os.getcwd() nx = 64 ny = 64 noise_variance = 1e-6 prior_param = {"gamma": 0.05, "delta": 1.} - os.chdir("../../example") - out = sfsi_toy_gaussian.run_inversion(nx, ny, noise_variance, prior_param) - os.chdir(pwd) + mesh_path = '../../example/meshes' + out = sfsi_toy_gaussian.run_inversion(mesh_path, nx, ny, noise_variance, prior_param) + #convergence of optimizer self.assertEqual(out['optimizer_results']['optimizer'],True,"Did not converge") From c9ea0679e6f8096e4a7dc651e1f62395bcf63e43 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 13:14:52 -0500 Subject: [PATCH 36/50] removed testing folder from example folder --- example/testing_folder/test_script.py | 36 --------------------------- 1 file changed, 36 deletions(-) delete mode 100644 example/testing_folder/test_script.py diff --git a/example/testing_folder/test_script.py b/example/testing_folder/test_script.py deleted file mode 100644 index 0acdd9b4..00000000 --- a/example/testing_folder/test_script.py +++ /dev/null @@ -1,36 +0,0 @@ -import numpy as np -import pickle - -with open('outputs.pickle', 'rb') as f: - data = pickle.load(f) - -xvals = data["xvals"] -arr_1 = data['arr_1'] -arr_2 = data['arr_2'] -value = data['sym_Hessian_value'] - -if(value > 1e-10): - print("1") - exit - -#values from arr_1 -relevant_values_1, relevant_values_2 = arr_1[10:], arr_2[10:] - -x = xvals[10:] - -data = relevant_values_1 -y = data -coefficients = np.polyfit(np.log(x), np.log(y), 1) -slope_1 = coefficients[0] - -data = relevant_values_2 -y = data -coefficients = np.polyfit(np.log(x), np.log(y), 1) -slope_2 = coefficients[0] - - -if(np.abs(slope_1 - 1) > 1e-1 or np.abs(slope_2 - 1) > 1e-1): - print("1") - exit - -print("0") From 894a510f86d87811b02137dfb259e6fe607ca0c4 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Sat, 16 Mar 2024 13:23:09 -0500 Subject: [PATCH 37/50] final change _v1 --- .github/workflows/CI_testing.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/CI_testing.yml b/.github/workflows/CI_testing.yml index a17ad40f..029303dd 100644 --- a/.github/workflows/CI_testing.yml +++ b/.github/workflows/CI_testing.yml @@ -2,8 +2,7 @@ name: CI on: push: branches: - - numpy_debug_v2 - #make it main once it is working as expected on numpy_debug_v2 branch. + - main pull_request: defaults: From 00fb8b2dfa245e907eca9b0a207219831e65b51b Mon Sep 17 00:00:00 2001 From: V-Rang Date: Mon, 18 Mar 2024 10:31:15 -0500 Subject: [PATCH 38/50] removed files --- example/mem_usage.py | 63 ------ example/memory_usage_plot_poisson.png | Bin 23251 -> 0 bytes ...isson_result_FD_Gradient_Hessian_Check.png | Bin 31765 -> 0 bytes ...qpact_result_FD_Gradient_Hessian_Check.png | Bin 32629 -> 0 bytes example/testing_folder/outputs.pickle | Bin 1041 -> 0 bytes example/testing_folder/outputs_qpact.pickle | Bin 1042 -> 0 bytes .../outputs_qpact_misfit_False.pickle | Bin 1042 -> 0 bytes .../outputs_qpact_misfit_True.pickle | Bin 1042 -> 0 bytes hippylibX/test/testing_suite_file2.py | 114 ---------- misc/mem_usage.py | 60 ------ misc/memory_usage_plot_poisson.png | Bin 25295 -> 0 bytes misc/poisson_example_copy.py | 202 ------------------ misc/random_testing.py | 0 misc/reconstr_m_pact_np4_X.h5 | Bin 239768 -> 0 bytes misc/reconstr_m_pact_np4_X.xdmf | 23 -- misc/result_using_4_proc_pact_v3.png | Bin 32729 -> 0 bytes misc/true_m_pact_np4_X.h5 | Bin 239768 -> 0 bytes misc/true_m_pact_np4_X.xdmf | 23 -- 18 files changed, 485 deletions(-) delete mode 100644 example/mem_usage.py delete mode 100644 example/memory_usage_plot_poisson.png delete mode 100644 example/poisson_result_FD_Gradient_Hessian_Check.png delete mode 100644 example/qpact_result_FD_Gradient_Hessian_Check.png delete mode 100644 example/testing_folder/outputs.pickle delete mode 100644 example/testing_folder/outputs_qpact.pickle delete mode 100644 example/testing_folder/outputs_qpact_misfit_False.pickle delete mode 100644 example/testing_folder/outputs_qpact_misfit_True.pickle delete mode 100644 hippylibX/test/testing_suite_file2.py delete mode 100644 misc/mem_usage.py delete mode 100644 misc/memory_usage_plot_poisson.png delete mode 100644 misc/poisson_example_copy.py delete mode 100644 misc/random_testing.py delete mode 100644 misc/reconstr_m_pact_np4_X.h5 delete mode 100644 misc/reconstr_m_pact_np4_X.xdmf delete mode 100644 misc/result_using_4_proc_pact_v3.png delete mode 100644 misc/true_m_pact_np4_X.h5 delete mode 100644 misc/true_m_pact_np4_X.xdmf diff --git a/example/mem_usage.py b/example/mem_usage.py deleted file mode 100644 index ec8bca49..00000000 --- a/example/mem_usage.py +++ /dev/null @@ -1,63 +0,0 @@ -##Don't push this to github - -import matplotlib.pyplot as plt - -import time -import os -import psutil -import poisson_example - -def elapsed_since(start): - return time.strftime("%H:%M:%S", time.gmtime(time.time() - start)) - - -def get_process_memory(): - process = psutil.Process(os.getpid()) - mem_info = process.memory_full_info() - return mem_info.uss - - - -def profile(func): - def wrapper(*args, **kwargs): - mem_before = get_process_memory() - start = time.time() - result = func(*args, **kwargs) - elapsed_time = elapsed_since(start) - mem_after = get_process_memory() - # print("{}: memory before: {:,}, after: {:,}, consumed: {:,}; exec time: {}".format( - # func.__name__, - # mem_before, mem_after, mem_after - mem_before, - # elapsed_time)) - return (mem_after - mem_before)/1e6 - return wrapper - -nx = 64 -ny = 64 -noise_variance = 1e-4 -prior_param = {"gamma": 0.1, "delta": 1.} - -mem_usage = [] - - -value = profile(poisson_example.run_inversion) - -num_calls = 10 - -for _ in range(num_calls): - mem_usage.append(value(nx, ny, noise_variance, prior_param)) - -plt.plot(mem_usage) - - -# # print(mem_usage) -plt.xlabel('Iteration') -plt.ylabel('Memory Usage (MB)') -plt.title('Memory Usage Over Iterations') -plt.savefig("memory_usage_plot_poisson.png") -plt.show() - - - - -# print(value.wrapper) \ No newline at end of file diff --git a/example/memory_usage_plot_poisson.png b/example/memory_usage_plot_poisson.png deleted file mode 100644 index 59d179e263cf1693428c942b198ab1271439601e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23251 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ⅈ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 diff --git a/example/poisson_result_FD_Gradient_Hessian_Check.png b/example/poisson_result_FD_Gradient_Hessian_Check.png deleted file mode 100644 index 4fc349c32f4f719f571f3b9079ffce164b100432..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31765 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 JBBZMt1T{B!RKx`FNZJ7X}oC z6k3$~Ve{K4Vnxj3gqaDIzk=uhwp|wxc8g*(Q1!cm2)x5rTn;)2hS8PxK{;MgTr?i4 z*F+ovV?{;spWE83A*W{NeOf%v!pdVm`%5%Fyy`vynA^{_mxRN?B5 z1H;=2w$caaWujZRvXiA+n4NTsj(>Y`g8VLBmK29^5S{x8@vZ#)p z-X$0~z$&iMbsb|p14k^!PmQs3G^VH?<$=v}^Oi8PyvMKrLJTw%;x=n06~i5tk>3P- zs77+02tPd}1$A)qjbT@}`TlQwr$4?{zsM1L3q0HsW07QfNmNYiA#14CdAZVMy?DA9 z{o9Hw9xC_q#U>|`TQS*S=B3`LMmeI!_P^R5W%bk?R0;13ubm)MlY5Tsa2Mql=X99n zM#T3@Vqz4KnFypGxE?i-hlzb2RHh;(Mu&Suur?Hl%-EE5F6exLke5>h4{x9>EWT^p zJ1Oh@-r~HhIsJSr%9=E!J= zMrH3R%NoG@cwDk?Y3MoBSQ0Etm}g(8N#?A$bcy~_aB$_PPn-Z`O76cKcsV&a>7d#n zQzf=x12Z0{fzlHA2k=bg+qcVsoowlxKH4GPNT#KL9o(W%;QHfZ_!(GYJV4+WFL;jg zeNzykT3&8$C+rglznOg)5sX9+(!0Bh4<1JFy?a84Y&%a4n>8oEwf1xvXV{ufn&0(^wQ z#!Xw7PR@a?T)HUj-P`4#pI^fVN1lOW0DqE$B9Np1b56=@cJ`ZFZcEb(!bW)TKs=~M zNkO5quCC6LCieJDcf=Ma$WbMK-fRNsJfGBqajf_X#+3nw9|v%*Cr`_e1bImODPTI} zEFdMolg&?jk&J(UXL0qTXi$gDb-Tj;D7g)A@oX_)K|Lu*Vff)Vp|WrwmY46}uY_B9 zr!D+)MRFFI_BC!U+q_f*G5Clzab?g=iMm19E8ED2SXPJ@=r7d>MUA$YIG*UNxClXJ z_`Tv)_0=MA?KoAVWF7^5Fx8~+DXPM8E>wkZ9RnbEX!B$W22JqH5Z@b^%t_+UnPU-i zN)Ls!cps(BAXLr98DCuHkhUbkOzuNpgW&-6Al%)o2zNPSFIeqlOW4~E4Q4ivz?%OR z4Yi8(C&-G-cExJhwQ?nPvanir-x1fN)gOy>mYGW+Ihm=wC5G()abr;vVCt`N# z@@2D2I@Pf9NG2=av19GBWy=7hY#K4v^B$N`hz#iG=jW>d3{|u_+N;#yt}Cxdt5Iwm z9D#^TSi*xE{qyJJZGqef{W_v`o<6-HFfg#_?(NhB&*cdY#=w}wcSNM`KA;3qn|L|5C0O`WJ9{UiVnt+?xpI2B|gAKD4 zQzK*=)wl*kF~@KBm0K)LX>{Temp6Cd63??Eu6P~V!1cMvS!{M{gn;W)Z>Pt4$e7NB(Ml%r zOpbUmTycnlvyb%azmCTrMZeRVW(5RSHf6qTE4?lWh(H4CyXyx<4U^OuFoZ;soY=FZ zha&%*1C!33Jx&!43qUnRF|S;CO@58&;LA+`Ht}UMKlw@72E_d6-QEx_#1ib>E!Lw5 zJCc}n2yN1^y0i{-~6%YNGUDz)gr!RgCkFputD_A2mr!9n4L-RMk|?wge4_)2Cjr< zux4TL5mWpR2{nG^kmh2>uH?t2-2>=G+*6D_lFdLuJwtEys#gIVJG@z?G9N&O)IjQu z$}TAU{+(Y=Pb%aDfK5ZOi|se0?!X+Ya3H_@3aZs2`wfFA7wz^pVjwIU7`$JAUm!!J z*t%vFQnwJkNLUlo8t4X0e}8|W!_Z{fE74qIJ*w`MUV;n_+#`+em;)c`L*7PN`Rj+A z9;QQeNYRmWDk?-5-VK15-!hP2DE^}9h$=d~#Hi_*KW00q08lTyLIN0GP$?jOJ&lsm z{m$xxh-wZVy&fPUxe-i@pg$5KJMoI~-w;_?###@W_-16X_%N7BOHCt9PO$ob;JsLy z-m{DI6NfFo=CA&T_odo=FLq&qaRdn-MG@uhhMce{(MU7e$@9Y6rvTO(`5mvWWx0H=<9}?isofS01vq0&LQxp)rO@8?+%IXpA4nXRBX~~W?9xNlpxQ@HD}|45{?}C`03L}Il4^tP@9y~Zvhu-1$-C>5 z<0ePD_CgC~MI4#z`#e0CW+12p1s4fW+CU68o#u)^3EFo??~Xh>LOy6J3rU^JBegid zFOHH)MoiW`XgNSj=tva8BqV-DX6i)xq9sMO!OPRHBGDbQG*H-YCsqY6_L0bHe)D7d z(q0MYo*`GUCDrp#3L}(`2Oa`#rR+EB(QI-M1i+6ZFO9MYA5SE6P{=tKVWt{W%R}w1 zW(a1#PX2(8HQa?N=81{>SeR}ot||DcgglLtFGJ$}cqK`)TdSkr`C_BNgC?}KxQTT= zAoKlTlqAjw86Fr>TabhOVqriXOP!4@2_#7ebB)c-*ml;kZ~c;PdVGQ07&qqXrW)`9 z4!p#G4WyC87*5#MEV+lhqrt&BLcljhC1vcIHo!@=6C_Np_&kD95j8cDG(X>+c(F4% z4qDT z-en$m3qhR<9IP-TYs_%sQz?l6n?_{KlxO?J2=u_o_v}mbXsZ9mBZtMC9LL4apFU7t zig0rN&xdZJc_8spUoq7g3Uqx{@(7FW4e92h*<)A1gA8|(S!N{ZXefLOlFAd04hwTd zZii#k@q{$($bPz zm-&wqvK@YLEWimwTPvV+MH3l>r#q!BbG6XdKV$X|lHbt`BN`u{p{1+;n&Ri^z&{Vw z!4nUC{CGXCM#l1yDKlg?rxA~c$HhR>XejbFlb!9Jd;5%v1*RQ?vKir8NUjA3CSzDV zXwJmnTaYlxM+6gNX3X8{n`yFNux;UmFySu6$`x2^nbO z8ua}6uyChmc@36c2bmx|Fyi4s?6AAj+B_zuj##_AXZ_jO>*2gug@(+*=YvsXG zf{=qmZWAwWAR~DwDsmZ?X*Zs`RjtWVJjd!h+#0k@_$G?1;et9D9rwsEWt|vW{_gf; zFKDG<=JnX4#L}WFkdbHS;gN7ww|Sd?&D+`aLhO8<&A{zF@>7E~CKWc<@8TstIJ88W zKzJkgAH-C6VE%b$v>$F>@;TUzHJ~RI5G%INs<$>4Qphu@tR~&c7rPV`y8A|*?i=rz zbh$G(*~6_WtBdFfd3Of^PsINO0Bu(C>sA-u!JTTxucb$S;NGbnp2;^RVR94o2vv)` zdSffb#6dRsPifuE3SzqGzep1;*z?VC1L}i22)oQkxlb`BxH4OMw=5}M@tHyseciL^ zDD_a6F4N?;jAgDi5lD zTPmPbU0l8ah59&P98ut5*~8f`c&hNRd3dup{pl|s)i1`q=f~TH+WnV&|IIq+=po=o zW-)M(D%;xlF)-Hh)$w-iRil9_dF;q{Z}9q&AlO=DhvRKbE%I6Rg3Mq~5q;r$NkqVw z-k-f4<4k0P9OHGcs6wA)B$P zy!h(m8!l7K&|whk3K}=nZLI8Dmq=omc}==RSIfI{%s~z!2?kFqz>!n8hHUn))rSjA zVuE^lkuBr@@uegV-i-wDKw5eg@ukrF>>@G01Jf>R%o($ghZojJxS;T0LY6*ISe1<) z>WId`mD=i!TzC#768T-wY<1i5fgeAR^}iVm|B{A8HKEv{N?6)Ho@u`{s9LKJ6F?nI z!|lJy3RDFWEkqC`0nLH-b?eq$GurhZdun%mZS97fKIx|rp~Vp8At^bK2Xv(Mq*A$N zj=Q_kOBzxJ2Id0ee>mb*u6u8UE+}4%RGJlPIu-Cid2R)S=I1TWrc7$hM$4BLK6Lg0HB=(3a^>1J2FyZ( zG%$eHeo~Z^lw?7`@JS6HA0OsJ{E98^Un)4=NQ?5V30PKO?zLX_+RGNNw>FR(RL{%C zm&I9*XZ=#hWgjPRr$CZY`0a~J$5aEbi~; zzwhraJd!o_-Q%L{$=BCa7G}fm-JCzrs3ox>>JSXMCM`+{FRy8U0j}mx^n`z=yKB%Q=_xgDZ}0GV)@N3rG_+I_w){)z zjN9w`ezM?w2Et*wemhibWcLskEcUGK!Gndht@qPkCSx>d3GgAaWAiKI7HjSD##OG}uxd z5nsE8kTeo7K(lGY@LYybj#}R>6VbyB#|BqX-jf_V=YM1_{Zw@9$;KdJ1D>qG+qZGV zd&lgX;s=-Yyoc(LeIaj|bHeZtnaL@b3DS}bX`1EV#@OGU=r|hn-i}jsui!t#AFm1f zw&U0eyvPgGXUFn|ZM_F%lDiLY;3B%K-_n8yatC6FjQ%IHer8QxTc6c( zqwMm6jR2C}2rbHy2{iDrUxGNNc)7)bfJY!Z_b`0&*PvPyMNBQc3khmrxMyh11*3`0 zoe*XV(zU$`$|Ma_DIg7XO-)fGi19QvEiHw4q)ktA7L>hvkD1(Tcp;RPVD3xf0}Jzc zo--B_oVWe@vcC5G;{iITPqRBx@oMj#zJe~nwPaZSX{o$n0#bWqK5_SMvp+wF zf06;8X{e!{!^6=yhUC>}YpnkqWK(hv+uVWNZvDMh3+}tM1{OipVRK+a(|D^70-P&8 zx0y`%_~df56-FrejXTa>d0VAWesuFz_7bY9a2QaEin^$2)1qQZZEK34ZIbK3f8iI zo%Tj9Dj;uXYL)mMnwdNnYjgqz1_2 zBg5Vap_bV^jOL`hj>2?0N&SH7%Xm+m&a{@_!^ZuAjGkj$0*lgpZqZm$FZ$op6-BN|q%rDql7D1T~t6Eid{@i2}^P70C>B*jYdnorPkT|u5N z^1eSx?kY31WU!l*=d|3L0gtS@4s{4jR~5tXNH}}qEstPQ<-0M>BXgn16Uj9U-15+^ zliaXm|44_MCMRd4r?A5>2vb53l7e76X$gvcDSG+FtB!Q=k1caz-xDZ<9P-+Oo#(z` z4o%9SFouOY;O4ff@FX|;Pp@@smTkA#7Ej|4V0tbFs%;VsF%Hdr;*vTFTk6MtV#tx( z1P9k?tXsph$Z3!D&g8N-x>ai_Cf3oTTMqf%%DUN1y}o{#rApSu=8_f{U##Nw@mvQf zy1naOBGHHUxfv*(svV*M+w|TuzQcu0^|imnzC99i0a<*q{8D`M7iRe*#MHOTrK44gw+mh6DfUCfAOr5I6$5I zy9E!)&2OqnGj45qKe<(1zb+?2T7f6&5W~w$cl+}tSehvMA*7N8Du#-PRBK2S8|hr0 z>ikpW*dzPSWlD*R_iPP)AVAq-v~A#F9=PoOq#^7CGmbw`4Q2Ufo09)?@Kk zS#4oq4;QcC@h+QPBXHVSXJ8tb%v)PgT1vo~mnxQ~`7*A&n)xAJwKsv@j@Ttco~n?<+KI6c6R&P2^Ox^GzwG4q|rgK{UN0zn1cB~GoD-xG6xn8j?6h* zd=Q#wzP^sy_H*QBLqYN2BK1Hus^x2v+@b~YU$Qi%@pT&NRrQcVp$%T11r?u|+MuQq zGE}2X>jnRo7RO>mJ;%&`?g*_#ed!}9)T*i;&$f(@Modq%gpJh(%(kd(%*Wt7F0h0>P&_hEr`)-$}xUXFsne+Ii z_N`P%?m=~MiNnwR5Z^LPVD8Gr)agz*E%vdx)C*rr>q!#uQ5zL5aNH0aOvzu&op!v! zVP9bF6>HzgI($1MgbHKvgvXCd8XAs#anLe><^=jun&S@lxVk+-9+oAgz4O!l{K}J^ zF+Gx^vLeZ5-e`l+#EBkx!ihP)E`)xJE9o%}mm8YbmbJs1F_<(rMDt>>oAY; zs&>sUDC?`mrC~h3)aly=?yjELT{c>($)&}6bzuv`k=u-3P`AF|C>oHG$-40GZcwQR z{D^+RJ)6}cVoau*^HUP0OfU1M_UB8ISMbNI3F3#t;(}}1pfeW}U8yl+{yU8UK?GD0 zh93X^{Th<(f&5Bx<)x*iubY~9;A&xHVtRvQ!j11+@T{N(=aM9p7G@^E#?oJ=X)f1R zyw6uGwc~*f{VjfR5jWfm>{o>^kZ&I?g%o)tn~ykL7u>77gSdxEkhBFH7XLHpMHHN{ z6yDo8h3Q=t2=G%;FmEg&Eh8+6EPV0;Temg{6&b7ke&e?dR#_x3{(D3NUj+?3jHCl7 zckQqd)c$~2Gq{I(gR_|6ZvaODzA<_VJaNm>kCPTg0~P+>sha;V1KAck z_+Jd7D(L&110Vb)4N^8QJXtluPMzPJc}(}`*Pg+A(+AgCr%o40_B2?%4O$i(P+eI` z03g7Yl?SN*h7E^VEoZb z?TiyJHC1~X#-;2&pElEa_9OjJr}@I@NVRib_9IQ^uA%~MqblosqqEvB%+2L)kOM*1 z5CLDye`x5`{a!=SJ3~HbZoTbS(s=x77=3GE`YYgjmSPsB+~>{+DnB%&uJ7fuNX( zrM2+4oX%2lw_Jy&$Ic%NvY6Q}?=~-BTB9hG&9>wGUJ1_k+uO3onPLMX;pHK7bj(66 zfiHksTI7~-{Q5S_nHh@v0@cEz60=X}?Aw1mO{x7tyxC|~?k4eFG3h7ISnf?L%1&11I)XY-^B5vhe zuDW6fQ_yD9Hdi;V9}SEWqV|eiBW<#_ZK7YlOV#+YSmm%9{|0g)CBaA1SWuJ*(!dbl z$=#C`f#4RD1oL{*^%qRfD(2`&v}j_ef9g2Q#w38c05b#>Dc^F}Y$m}hsoc}FUrL%@ zozN3CDBBMV5RKA;v_3WhL{E!h(CEj97Lfg%o%m7oK`4w_Ohhpo2 z03S#UgdBjOh5k2a8FdXGV{%xLVzP`itf=JbuhNvFh53X9e5{Nx zMWq_OU^>B+Jt5lFr8;q6kiuyqQu&!dqw!(JyY)wpve$ocD3Isk;ZPALH8}32t9C37 zAF`-qn;3~xFZ1_K^YTkIsEvt~EPhi`=7dP4wM}8O>7|*?)o;Q%o;E$|>5;M!&xSHi zVzkgm$gl-ulg_V_QY&7b(=?&kb|%c`xT|2+E;{qU#l_c)zg~B`->g!%8Mx^*%5JsG z%e$;_bav_LB&5G0>8HIyrs3cY!=Hk*E)9i5!JTz1(NI)Q-w|h6&P!Vpog87(mMi>K zv0tCB51jOOHwtwTCIu}$VQs;bdl{%1qf zs8UT+enkuTRh13ZTbZhg@H~o3Z|p<_drx4}gWsdg_uUD&4vgaPJg|VAPNMl}W8mrZ zTV8IbGLV>I6SI8YL7>d0vvC$Zr{#25vLqQXQ;vQG^aXbQzDjd8v3Yso& z-x?YDw!OH#Tw%moNB#k^xA30$;ru>Z06{Opm_p2$Xk&1+r}Y}{^6)#qDOH=1(%j;2 z5Ht6aq4hb{6}K|uyN9!8Z0_HxO8uAYbFyx+HiV?HYSMgV5p=XTjJgyn;E7#1tZ#Ua zjiM{oux@Z{g$=vmpRbB~lEer835uY+vvVEvwWb*oD^0%2a+^Thar0kCXaShueNqCs zhgS}Hh<+Nsy$1K#l=zp9_oeCe4rGRMtw}8s;^|C1i#|R}5u`5N9&4P#^I}l{konndbs|fbY z@n_M}>V+L3UlYeD%07~!ku=U8GEwIFOH30af}%EQT`wdNWbE3uNtopu_C`P5SJrSu zVupCAtr?&0*|OHnXO>DpP)+AMt`M!zG7%5qez;BPgPhNuBdpAo4_&u|X2N^nYg@9T zNoPW62-U)zc;`;`5X;F644t`&O6ZoJ$8Sk!>}<~LF?ok`6Iw%tdhm>UnMmz9efml3 z1Wa>8$cRtU*~3OlI_g1JbrCM9W)VU4)mch24})A_9W@*YO0ddY7W?N=I$iP6VKGo! z({pHVydbk^B9mOuR#ZM9rg8Yg`LPvJN3<3e#&TYdeYSEu#qBS@9CysaHFMk!HZ~%^ zLmVV&0?XUsBwZ@la>?u&;4oeVyIUwONHz!x%@fn&#!*Z**=SWyt~`TRwq8zmk-;F1 z3EEa&>S~8>YTbwQ!Rdz7h%t&^H?fQ7Z#o)!xJHp`$DgQyi!a3g32NYs+{yQANOTe< zq*1Mt{(8aV_1UgxE7=@d_L0s&Pw3LfQ4vqqNducClhrs{mvNPO!w4On_*dDXT9HtA z)7T&WMd9^n?AWr^N9}BF!ic;>M?z?H%~mDrS2eR+lY$b<)0hYON`}RM>g#qGX^Yef z*S4B@OP5&T(H4(y18KvX(#@sMrJ>Qn2{`XzmtP(pY`?RL>k9?$eb_M(oLdL}2|P{-pOn>d+lQ-2!Tk-zEvyI*q= zQc_u_IuuEQ3<6$&3~wPZKE!a#sG?s|qR^Q+d?%mF;+mlkbVDEaTx^#TjPJ1u8G17z zwkd*7#DiM^FhkgAi3UG`$%xt`;i!+<8HOSVgowCnSnIfI!tTyJ$Y;CH;{0>lPS>s@ z`mzdbv{nOlorM8#y;CSi@)3DCKqi-)o0)p^`6QPZzsm6QT>cjv*p$zx9ujh|{WWoh<#E{lzLiy9u@rr$NZ z_k#xyAjBE!XbkAYv2pCMrB&6rl{ze?*%2D;b@9aH+MreI?P{vsorCYL-piyzdG`tS zGH6GV_;YA5epak-w3JyHvBfx7n^cO+I7(^jKDZlXUBNl?JrKRk6E-3Wg@o*&O%4Cy z^Xh$LB^Vki*`8>zhcinkpZb(93~d-%6T&$OXOg-l$;@=Vuga%Pqr4k@PlOtYxepo} z*#YoreT1a?w_OR3Zsd7O&W0sP3g=nRhEpQrKt>_}9QXzH#T|E_rQ7DHcJ}AMSE&Q> zt}ipfD_!s`@P*lVFTZcgmV~{}@AD%jbF(5WS1DZkTxIAucSZz;1sgCWOt`3XK^|n< zo~8|ZZg7mosgpq5V4rn$l!||4ZXcIF{du0m+Rl_UE}2TrA5MqUGtkUEvX!b@@AQVY zP6x#sdet7V&@NSM;*@4eUcpCiQYg{bGB;Vx)6X;agG1T4N0KwXM+=M>WTBuD5w{sO zSp*=iECZsd4_2nYS?PdETkzNN{{#baTc&AOfU7P0+>MMwvm?l?Wa&dhOXm*-dNF6HzGL%ch?Q8+$E0Je|dnf znj0~^yPz4bU?(?vxyHHVTH*HDX#6ypj~)G&H4bYf7(|jfu9!WwyZa6Ncr9Nwn&-gc~70||5iv7 zv%@vBdV1V)s+p^1dqgU{Z(4o$0K$<^IOXgQ|)l^B%1gU>_=0dF?I`aRd&-tMUZFp)STd;sXiXbi; z3?iL>Oy1D2Z$BL=q-&haqm;)}WnQci>?C_^xu=;CLLU?rd?wN#e&S1VkRm`Tr%-c@RJ+<~P~4(i}TO`ae$d?!c{zqSCuz#}1RN@QN0$1m2Vu)%SYn z!`mEtuJt*7WPbI^tE)e8S5yB|a8>CmX@#bjj%PoMEJ~OPgB_VG3)h#fcn(6XpW%?C!dn0tCvWsDl58@&6ESq!X__=gd zCyQjqsJGoEDpB%2K}zQJf#3bb0Ayp3dKjhNh5VmBN7q#T{!H88@E`*3e+za@{oBH|)~(3pH1i?y?P+(AHH>O0O++u;iHj z+RluyF63isS$?{VN%z{j;;%zCc-&;(Y#ZtCy-_gKBQEcxxB+)MH*fEksO>^=8s&Im z_&ck!p``u5nH$Coa&e40%zVa>ZdJ?CbsP{%RWRN~*mp{7OAM z(X6sHb46@O-w*#QGTQ?f99UIM&$ex&`kcczxl{V=XZo~Gbq#AYBJ*!^D&wZYI*8+WhMPoGc>1G#J)0m zP?=v=qB$&CMOUF=&HS9p|EQ=(Fk5@)l&n)?cA~<$S2NGd-1#dj+0vy3CWt@g5cfk7 zvA^k10#uyz95zrMQ}qq4slYF5L^5&vcf736__pQz(;DMThsM8ZcW{*+RHma}u6t1J z*cYEvDh`zmb%mnMYw4C=P6hRP9T$v$bGOgrSn~(Ih~+aq%raDc_j$79O=wQhx+*oF zmo#h4c9}U*cpd(QJNU6Xi3%ExzRO})a%Ow)XOrV$9drxIz8G)fpravlDPr~rk5s{W z<8s=%z30a)XgtpKd#vD>3mOq+VU(8sxOLY?LD`FQ15FeI-w`0<7A3*%h9&IjptzqkgZv!t7wv z4K7cN$FB~rSn+ylP`Z3rjLr5`QD>j^`{G95bX5_LvPsz`kHYyCE=z8XSy~6^F)$2z zu#HOYj7VdSqPv;IP8E&0V_$^qaFyqAiF>S9mkSzf7i+w*@x+j>MN3w^8|$%Mh4ik> z&3*JMIPHIQ6tUJkSn824%dqU39d|(Gr=gjsJM{HlOp(g@1MhFTUTjyic*i>t9TheA z(_xt9m{ORhjLhZBaN@?AE5qwKlCG-yp%h%Ns^#tVBmH!3d5^C?z3SFAdJN-5oVL%! zw7S&OOTUuOnIC9c*Z{-jJ{ZvjiSRJj;(N+xr&TI z>6Lo*oEINU4Vq?c)uHUa<+I1%Ps_}bF8)V%zIL|XvRMDjanY4}RpO~C&)0AJ7AlGD ziB=Ljv^1>OZ?Po=vJj?uI5b_U~~bP2~`+~PusEjF#%{{_*1vcFv$eJGafAQy};q zS!+fzF=Q8pnw{NK!XoL$I7m zcb32Bi$pqyLIKCk1z8Nz{UM?L5D7~h{YTtp=olCnXc-u2DCE(9`SO(A8MoQV6ARzm zm(*c$Ok9zu3rnL9e3TTDP$HXEU{V#Z)tv8vJus*p?|?_KIz@)Ga8 z2JelDqsPAxDwUm?ZEtTUXByF<*NC5D{6dHZE#;SSFaW916Xwxg=FwjBy-UsM%63 z&geCcLK#)8Q!y@;5}5QrK5>9U5=^qsXM1g7LMN+(U}IGS(h_1NhJ(2{IM`H)U7wsq z;va-<2g=m500TB!k`9Gsqzpr{0QSdHzNPCBHO2R$7vp9^w}dKx3mw+&^1ls}&rx*P zWxKuMS_~vEh=$BH~fJkzJl-ozU@X z#)1=(`uj>RCYRKuK>FXUEnn#n zt)SyQwY#b-KQ7RKB54BGy}&&}3w&~62U;G$xv)#KeO0<6?tgU0E-i$;`LGy`d*I*y zsgLJcH|(S<+1Rk+%cVi)kA^|+Ory@5%IC-P%TN4mg4=p{!k9^W-pS_Ae`@>Wcu1=J zA0~RMcDQWrJ&_+;VH})TVZTXYtTtjo*33;}=hbIIKjf{&+Tzr&(^7cXLCSg}gJnqp zWE~D!T!Z1<8;p)uN%wBO>B5}#eMZtJ@z?e(X5}eCc%eXRFN=ZpJaP+9(jcJq|5vM` c!x=w)hxXJ4Uq9feEo2R9VEw#te&FSPBx05HVggtCsTu+=iExS>1 zo%;75*m%apZj*dYUIsp7iIdJ@SBm1WB>&Q+X*@VVQGPwTTHB1g zZjE)EH8HZCW15_xXhkoGhFuUINVspY(k3h{DI|3FlC*UDAln=3Om)Mb$EsY5KQf*k zk{WkpRZlqUajPPY4bmITw$td^9yomb{Y2DRK0Zqmw@b2vZ>L?4x|ZE?RdkJ#;h0vc zn^%q6&Ph+Bg@0^h-d3^)gy0_sUT#fM{KH*Ae+7ShUAdV?9DkJAqECh5&-zRMfAH5f zJMS4-G(9?;FnDw8sSDfhmBvXMYlRQ#z9=lT{&06&-a-LO5N)i)o`*l1*T`&ra{L{B zhEpX%>hfv@_Wb;O{C%;{^efpq)dM40yZ2pMa`)|33DxCl$KS@^Q+nUgar8;Y+qX;E zR>^5T*n4}kWjXzyA0PM0`*lpb{42O^+cqZ`mkX(>Yxw#3KRmJ9xBh4W&x;o?W_`nV z4R#j$#LGLbh!jvcl$`kH?c2&9Lm%r)&iS;owA?i>_MZIyy}an`l+*P1(TkOOMgn;q zkw(`v&0e@L;Bt3gTYt7^dd)!&LDkal{cpN^d+B_9d`ylUNj}w%d*xH`h$t1W#G0si|!_^;wS6G&dLAXkHXDGvoE>$TRi>5B65&+P-<{ zD-fp_)NJmm`>I;VKZ1^lU06h9oBx@|jISd2E^>+O zDy^-pwa9Xw=u~@qM{RY$EXQH@KYym`Rw_CPf1jG#;{C(k>+D$?>ir$H@O>KdJi5BN zioP@1_qIzsy!P2aJE8-h`Y3Lu>Hg^G$)zhdQ|s2PTPbJHx$E*Oh9gIg{P|Wb)bher zvgz^B@T4Ts{Z?WptZCOCIDh{9*Ux#5snyS)KezbtwcfOC#YVG`{mHv5e}C_{|9GEG zV(%@JA*B@?Oau4a+-f=eDcz*NE&Y%}P2ogI_)Uc~R&TCth`X~R)$m?g8go-q)4`&Q z1X%@zV7#8rx;gRj&f;*(ic4kf?fftQd}saq>`Ydk|H7Z&CeKda)~V-GGdeOBlJg`x zJ0K`X(SKw0<<-H*{C+bOdQQB0v+3odyffqNFJ`_bM`?wIr$iksO-Vi7QP5K0E{%1t z*?+;8`NG|)7N6;H%kJ`^uPr%ZD>j*%Szgx1;+rgE60$!fow;FdI3w#rjL12|s?gEfrAa!4kJ`f4=4ZzC`v^ z!zG#>YK_mGt*O${)+h2EmbN3DLV3sEtx9?Dz`8Zpwm(LL!h$Zx`!YK{7s@T!H8#fP z=;)~W^V5Ol5>1{HomE%Y9q1Y!W|5YbetNoN6)W$?K)uuk<-MtfmQ`WPzkW*Rew3M6 zk8dFF*{<$RBcB5G_ND^I@#Dv5uwtauBK42#`5aw+u;a_m+bW*rZ{D!t!h>($7EpE{ z4qPsM$o>~EFR%5l(N^+`$!DeQ7sK19(~jh?=RdHYt?Sk0)%6ed3CW#kpg(lz(AN+5 z=t^dK!d;xNHBBBGoSI8Z^qXBWH$749m{oje<8L}jS~`QJ@RhXV6TNgJsmjXAyVli_ z+4=dGVq>{Gy?WW&U${PK|IIF_8iGA}Vu3BuXI$yzmy+U!77iixrK?x3j^I~Xwr0(m zgC)if&*@XPQj(HPt*x!5QNy`;c~#k#6<6-uiTvU>w`b5L#QM2woMnz(unt>^mBL%kxnD1|_2kO@(;Z=%N1pYhUN>*}J^SU{w7_sv zW_Wi+u<_I}*5|&zWiD|DMf%OpS`Ia&+P7qj;1NHFd)is($x-b6vpoM)+h&hZ6W?F0 zc3(S-RYb+axVX6PxtE}b?3t=|#R`w#V$FzG(;nS#SoAGnos8F=qL+!%<#fvAte`jG3`jLic4&ZcQrfo;A=;r!rnWo=}O1(yYXs1 zTmSs|lkL*4=li2x+sW0n;)Uy=)a7Zcan0}*8z#Ghg{*MBPVVkiN6r)np{lx6V*wnA zo+z1P4=A{rTJX@}iC*!ySF61yil;vm`*@Pq&|4iP5Sufv zU1_4D@WIcbv@{*Bfg5&#>gQ(d^HcBNXQ2Y0b$ag}X+e!!LItL!rMdhSJaOX01!3mx z=H_?M%IHK?Rn121WMyRN(G4=kEG;c*Da-e_luWBVh8lLFqco_#bRRkNqB&C^&y|6L zBg#QQntAxIG_~E(Fy{D?Lx-+e*xIhqzqxhKlT)Z*+wpz_HU9oNFfwTOHeMwD=1tb- z$49y8w{b=as!36t68r9$jpqr@`+LugJ!9C$8F=F3{UzAS<@fj8d?=uxYkVqS43~#b zY547(8dVjJCz>@rF^du`-O}2+1p7w$SX)I!a>KMR^99c3%MXqhYkWSmPP`{VOcL1Ow$fLh~yL$+_*Nb)}d-oPnUq^jj+p?tD?oU zp9;$FSB^tRyz|~#PkUNjd93vJ)F3HSnbj}3RO#RN(IkEc4$6rYgw@Rz43;z1`>uYmXV$;jS>(_ZvY2?Sc@Z7Fkzy4rI zWNk)LOrn5_hp?O+&zYicjM$xYvaF$`~7~^M+c?;8fyzaJ2fC8FTXq}C@5jfcJ<8+l=@j z|DFHxH!l2{(V;^ts4lF53s0WNq6I(n?ZB4{YiI}^^ZmxKE0X_ka=h-WL$biSgIVTk zGjns-+sy0v?=;RI^&iQ6mhITQk)DyUysC-;)mK|bhnaEd%DYM)*jOuNY?wdip9;6T zbC!{gacLMxhCI~Dc)`*&>E zi+WKT4&L56g7@&cre+&JsFbR9Yd-B-jqin`*xD=AyyY%jym(k_J(oU}zMXfY8FhE> zfZIBOQm5{6hNXNmT2DHfnht%ulBnpMRGzrO=cm~)w#G_Tug%`SMnex~9J+V-$#JZE z_N}MeO>E1Y-Q!k-kbVI~wBgy`deh}QB7{$WDN!>#aA3cY5&iDnZye?$9xDdQRQ;5S zW5MFAn45TctUCv&GSm4HnwPeNgVdqNW}#QETya=)Su6G7$4J#klxqdF+w}DG8B|@T z$*yhus$QI^gq1Bh);VXVj#HX-chtT$AM@{SJM*#%jU~r#-h2JA5`MhlS6GPV-M`V_ zHvJfD;~i^zRt+3VFvRZNyZIHJD3s4l3Y@9RiC@C5=I0F7B{mmaG`TVS^f0b)Pl8+- z%Jso+zoVh4Zn!I%ZDPCwS3H7kmqVa?LEfK%q%S1U0h_yrY!C}=-BvhIpA)F?=h^{at@6@0d3FD7~lAi2Oxxw zv>i2Si$y6VE-wD*xk{M+0r$4(>ir#G+^OuRPqhpTI4@kdaMv)M0B*~73Gy?2vHRpg z(=T46dle-V^{uyB49iu`=U3&U!%y5K0d+|bvhb5@VjluY_ zSsA!YUL@<%rSuem0j6!%R#p#vFQbW~%3GFQU@#Ty(Ca90&-nc9T5e7bO@KpI=eL2I zXOgfwmnyReJG?pL=~1#Upb9JX%$~>wc=l*z4XjG?(POYP6t)#~lsJVzr}k^73|~$mHo6 z2p;slo;f!zZBz(9Jl65kiK&Dl?$RPY}2J-@~JRadKBVpRy2*avhnvqBFx;9hNTh8&mv zTB*7Nhww(~@9ki!tg5<)4esRRR0@`x{N(g~5TBMOb{vfC{LK6I?aPeQ#3K(zg}-~` z*=hMO8miN0Ld|!&!;`QyfI+5Bn>L}bbp81~)l%%E@Yw7Ht+uxI%6V($3ec~|ZUUnV^K*{l z>q`LbUf0!C7r2jnNYUkPFFJc5Yr-9@@9USB8s^?ZYp`GLo%vFdW}GFs@n}IH=tj@L zyPNXn5?KHaUcHwUI0XbQfBIzfW8#aD`|zi-)ZN!Pg@mF2(k*5tyUAk)_5Oxy?0qqK zry^~C@`r589e<`@{;h&wAlLELR!Ld82MzdZeKOOZ`FZD_$|c~cUq=!z{?L8;t=fh& z^`ioz$#H_l%JMYSwtEK5Xlewp1Wislbpz!;{5CS)`a~N%`Yw5eW##4D?LXW-4`|O} z`K_m_90fTqa_5bqoq|96uODMM_4z5+qeqVd(MnlSeIGfoT*4b?jg{QD0t7%DEN~wx z*0RUPUYhsIZBAXr%}sD_S!bs?2+s=BJcl(As}=U5bUqr0&(7X_4~(bZ>e1Re&<2*6 zo0~(&u+4iA_`j^EvxNFxAonEzaTUzJDEGk?O-Z?8a4;MayK|w*G z`?5kO=jqO(bfr3oDD-E}oVmB{BI9j_l1NM+xxQFIht};<1hV6FP)ojn_^}v^Tj?FB1 zM2lZA`*Va93wc?m|KD@nxSjsa5o}Q5Pnk!Iv1qu9ii?A=m+XtLYybVb@mR@vImae_ zFYucn?5=|&=M2XTnJ>`Xx0fWsU2@f`|0@B{Y)RjeG$X0@=gxE}aLsdu+FC~P+c*K( znC$EZCC$SNw&sipFS~Bk#(vI@d@U3(%lrEM`@MbkA^Q7HpWbM8^r(E|&UA~mZQ>i#^Z)s=s~#Pw`Z-*Z z%c4IO>~HEpa}=xO%Nj|DzIJ#i6t9L^(v58Xe8ADcvrtU@t;njd%ds<5ait#*`P)uxw! zE@0L5qKz(Jw;c~U>9eKG5(|_QQt2~K*}cED4I8n$r-v2|6X>sJvb#cbr;b*ti>qrD z`}q*@0j#*S8#k^{Sz2>Ni#Go1)xf)Vg%wp)c0g^!$Mi>rUcMYuSf~P60eSACtlfK~ zvz^$LrBzisr`mxf(JE$_-VSm23aQ1uK8XPkjd>eqC3|p)xF#ok5vteM#tdF;){P#D zF~Xtr*>~?!f`Wn}moH~J`J0*Xmyj4$T6N@ z&PjSGYKe2L-g>eWu=^VTkjRD&dq$naw=G*@VPPSRk{65>fAG0K1RiD%j%ol%(iI+= z7q8m7bt|6nj?_VS509pT-s^Ynu-n<$-LB?cu|iZq!FXhw)~pCKt-1fa`=9ygFHWAG z)gUZ)4c1n&hlfP86Yy;bv_u5}*WZKKBdRUV7NbK+A22csyn2-fINLG*#*G_gU%m*T zM=QCr){~xNP6G z>(aoBmoGH{t)06{Y2?my^5grnv$IP9#JdLvnb7pWf!Qvvl5hBJit2?PTd!(jr(>e;i0K}w?*35RBoh?QM1oe7zSaMJVozocr%J%*@4ZnPhn}CC z>?#8$cglNjX=}>>Y3$skoi^XGkhqpDy8uZOK%O3|#N!qbu4wv4M+;Y|c#H-BUwMqT zFF_;C*%8B2^Pn~|GIFzmg7)#__nUsbeXB2b>a$TwT1c8Q{WeW6FU1Am=co4d7u@}| zj5a{A3?H!C0x*aU!K)-X6zxlEf8xXv^l6|^`+O)}_%G?^qPuo!9Xwp@Ef0Fd0KExV zpscKH+vt@L9okiL_5piuD_er70Hh_fbnC>37X(aAO*#MmZu@TP=~KCs`}fPs%4klX zJ`FQL)5K(DfyWpNYAun`uv#Ie@NRJseWV3|DD6@b<>**RKN}Jdk7;P&PVk z!OX>npJ2VE?9PJ5Qt>JMKo!|H!fI+qp9Ij2d{2zn-eoiwwLw9F7t9r}@BHoCw_Cb7 zZ}6x4F{Z9kh>j;(w4j=|1?mNn+upu=Cp2gFXKC$S#w#HywqQOG4L3c1yPB)V=>v6MMd4pfXMWuc=?A{bAaN*>npcGf*s-W%XA>Ud7mla)*Op=<1#KgRG z=_k|H@E;dZ3;EjXleMEkw9mfpiwb$@GWY9LU|?Vu)Cjl-*IK$?v%|xYNh7$hg6O-C zj~0HHx)btnXR4vd`t?hp*X+O}%CWB(#X1r|k+sRyj!;Uq7T@=^ zjYq@I&HXB?t!4T4?VE(k!Eli+u!}CjRXS7hTY^{$WUD|sANa(kWt0kBAu1!Yj3VlT zm$!G=>MQy=@)DvKXTHDLlyh#*19B0eSWa$kJSyU%GA@us(Zquc(<_0Mh*N?K+JFm6 z@dT->Mn|Qi;x+u$3p^(_x}Y(P8c8iDOa#^7sbe#{W7DJVfGu=Z=o;Q}iHTY1bKo15 zJ`=@~*!$u%U?-IjXoxaiSzTQLRy=ye<}#k&&}CufctvN?g_&LsVif=YKAICtl7jZs zjoryeJu-W-jI?Vd(cji@^tHU7fc-&CxlDG_sl$g4uiRoqPc9`=ARH_#Tp&gloKnl% z84R4P0BW=6xEU1a9ep879br8XIbYfBYd}q`(@F!pWMK!r4J8gm`oe* ze!gNems-w+3$)}_qLHA^l`;wW3VY} z6J{m~E}?#4NJ)TbR>3wh=%Mx8Z|dr_VGeYSjz&PuP0;E%hJAEc3SN{ji%>I}B>GqXiOu;vAuD*5q zcK5_Y)S*n%#vT>nnEZ%{rO?SDQ@KUS>x#;}dslO+I4Fn#1ZIPkT%)mp0mZ?=QTgiC zq30SKH?pHshlGWdLpbT`?miDlYGZ3lLlIVl<=9;tC#|z@Up4l~m8(~cZk{=>lUuv@ zyP5L$>kdL#`rhjq7#L8BU!e#jQ=g$?nyf=Lmtt-x)FR#sM)$SC-> zrnB#{@PMi8?+gwM$Se_$jlFSW2^Vtz@sV;1>|EJbgM6Kbbj( zwr>CVX}ZpSF5NcCMBfYev0fmJqLnhsNKO3Qd3I3XhFG;h zKn4)9gh2>rI(zm6K{;t}Z|CIVIu9=6HuiaWXJ=XpFdxD@7}%V=jOH+6%}Q>4H@R_NRr{9ebDW962fHmvuE0S z_p-y~0M2_Y4C2BhBqU_^+==Gg?2j@CV|aiYJxr*SIlik7?OO8Rz_cxEV%FgIsBU+c78b?{umat?w5@G5)U0JH;tl7qc;a0DE}TOZBmLgb&yT29i=D9RB8PhE z4Ou&4OAt`e&|o-vMTZIH-snAXV8OtWbz>!3O3tZ_EQF-#rHq)F2K&kr?ia| zAe5q3tXQGHAbtT?c>*#}6+XRe-S*HlW!M%=U0q$*n?4PZ_g_{HhDA&LnXKSQ_<3$Q zOHj&nZ?UA$&I^Cek}@oP=#i$2OKwZI0b6#Er08h-3vMFxqW#M23Ulp0NNh>sDB*sz zL3r_69RjdG0|7$ein9Q)7foqv$uHyKA^aex;_fP|-TSmwDxBtr=E}8v`5KS4=w4__ z6^py*;vEXFi`xSa_1!cmac~fW3PVVm3q+9Wrzbvk_4fxs1l$rd8GntY-g083zSa4)Lq3^?y1&FF$`hqd3do~tH? z3#Ph`jTPEOz?CbUef|Aq&CR^uPZMJ*Qj|MF&Cdo|O={>T+tjLS&{rmT5kE=%(Y%2T0{ZagY-RUSzXXh;mawkNw z7iJaC&AQ!E@ni=(u<8)vI`{@yR?{EZuyfPR1G*2>0|(EWFWFj;=^FgEBGeAx3B zFUne5`OrA*q33NVp%blWgN#NHv*yw1=AyH%i}&mK?||b>r|OT<)&qSZI$y+ZQ!99Q zN1S#9_|bXz(>nM`jRQx7 zWzvYkePDVT!2{4HgT9au9TwC4lc9LFg!f_}bE@#mP`cA}42+D2W|>a?2T!D-h=33L zl4DuHNW@Kzg&8q4c{b>AR4GBD00<*R4eQpg*Rr&{H*og!>BoX!*rFFQv9nMPiC;YM z{|r?>qOCzv*fmDdU{hS-0g0MXIA(H!(rX({+qC29x}^%p)=Se^RZQkJ0YgE`13Kot7u&B=EYgo;$+=l*|7 zTJCX12mZm=Q=dE+(Eg=e;XFv5ut$|#=S*Gv||5S zo_K{bf_MC8PrzCXMG@7-bN%Ey(cOKIZ>8>ci6Q#Uupi*?ptCaE9iP?Pv!@CLi=u$t z5Q!1kdZsf11j(wWGIZ_QwI3o_Bx1y1TawNOc4!s6U2E4k=p6yaeha_ta(3J^l^ELC z^~A2OysmEFlqzHyiu1qguIYu|Kc%oG<+yJ=nY#ooglrUAkMM>KY)DO1LB^|M4|Rw} z3~t+~aEjEwlfUi&&SiV{@8|M8cg`8@-FNgE9LkDYug_*s6_g~Mks}p>B zvUM3@`J{NVXEaU9uKl*rLX;M9uk+AwO3TY>p?fY@;YU>@N-%sSAeTz8E`6!rV)Cer z%YGq!VFe~Y1t3C}gdD&>^$zc+9CU?AQ|+)XSlQ0Vkjhf}D>_M#%P%0iLqm}?h{%ph zA*(sL_=-{Ri4y{rLZ}*Svke|P;>*a#8(~WzbJYd#KwvLER%%iG0B9;NFLxUIEKia* zKsQ-#-{@i#$glhWRajgfxG7FvUM4Jr#k%(l_++&083z_2yb6WZGLDp2xFoBRzY9Jj zsuoZ%UWbHRdqBwRueVjtE(6-3KGA8as;O16g=kgdN4Mj~F9Basr3n6PcKd4JOb5~O z`t947k!ykGLeeqZ68fAf;xe=Ve&R|0!bh%y?01l)S#S2-V3B(gPw_v9o@Iv+&6OEF zVXgapaO5iC_W~-{6=;Y)%txmy2ND2^L)fXLrTa=W*qAuY1!2<6s88 zgni*6Sp{#~bqO7L86W}Y>eb=U%Lp;B3f`f$YooL@E5aXwNDOT9SZkBw#dPI$2vkDN zdj_Qleo-H=@fT|^8U%T^Uq6Tt8^zh#*(z9TSBNJS8qtEj6Nm~ZCMLFH)Zi-@z$Y|} z9l`7ZAJPv~S;W~S;iEG^44ri`{M9OvXqhd9y${Mqu-Un5pSYzjt6611Ah zpFbHW(3uc^uISmME!eVPf{c`eztK7%cD@`emQ^Upyedmub@lYN!*m37118LdR_Ia< zLRQ|LxBJNk@@o48Gcp>SK^@q@$--zUVREf->~_-MdC_ z&Y=pFfyr9wEU3Er@tbKt(E+Mrn6WW;-AzeZOgVwofxKOSDH0+OQ&ao?{UyWnz}VQ> zCJ#wT$#-t5K(Z#;7Bs}v$M+BE^diHCenI5z(a(9r@4y;92&G92DsW!p<%kGS#X!=cH^ypuTcOr{ zdb%1qP#OX+0Pg)e9(Hkdh?5X`#AIa;IQkqvE|IYH)U3(i)7;!1 z)$=LFh{b3^xux6->+kPbjct@}$-6l^1n7eVLa9<b3qzzW(SH{at8%FjF3#y>so_C75>D1f}oZv7tSI z;_N`Z?H(Ju93Q_DJJRCM%s0_pd*~7H5+h9R%$HYB>eSzOTwFxT(QgyvS&>l9&damy zIJQB>=hu4V%;XR?nL-K3KJif^@Y1CoJYW)HlaZ0>f)$JH9=CescB3}+Ka)GKDk|Q; zXD{>^vpPYewiT~gl%!qnk0W^D{^(1(E!@FN!pw1kvL_nEp&+GS6BT`!Ak6&Sb3$;D zv;hUi0SsTwVZ4%d4XGAus7pkHL$qdB#7dDxxeFO!06s;}aem@t=2VD;0=^r)Avr&g ze%0xAR}+}Qi*QGJ<3_BePpT6Qso^2@SIqDtAKK#gN#YlxisBhwP(iY>U~|iJM{^I+ zacd@-#v~+!6&0zzb7f_{*0pSZKMbPtKs&uKGl@Zn!W%KUiF5yk=VzxRXlZE)%|j_* zCe@cP1`0j_@ivK#FLm_vm9+cV!;Kdp3_+%0LcfVBPN3(yaUZ)bc~S1zCWDB2+6uR! z1_q!E>>OKytxtdfl5&bJTg4t^6DA*^#b{(^_ULzKeLWY+hlSL0M)Q#M463pn_}00< zb~&EL4d_lb!YHry2)ArK^;w*-EH07=LZM`*;?>V7w|8`)iP?lN5ss!oqbmAdD?Njl zLk?OZl$=mhZN3-8Nk9{ZUW&q6g(xWSg$?|xXqvaca$uu7k(z=h%<*pxRl&BFhlt|1 zI%xP^M*D3H(Di<--?D`Z7f4INR7psl2HnNqmLjf#o|`(CX1b4`N$e1&g26TjMmkGTSRMU;q zOD%_-9N^geI8-1E-q)~-f0TrOX~n4n_p?>vQ^o*T>_4S(!SWB1*pq-(I-2d9a{PZx zCSzij@oV_;gVU4v6KSJ}>V5*5Tw)4snndmaPDxhADp-uQI?B;=PF;^PV5GURnoBn> zDvATrM-HTd_#3*jby@Yl2UeGM2ux1V?YeB}@9t@Q=0TY5tionkcyOUB5TTZ8e|fT1 z2I_@1D)-_t$uehykV&Kls;j5x0zhavWLA_FB7@@79lr@&;VDMEhP)k^M|5~*ITg`C zHx+*R@JDZ{sjF-Do%s5d84ZjpIXbc7d$jf@(cN#fsf2rwhELFe^Sk(o72<(@%r{>KcxKbZdDPB}{IW~{569nqJMzx#o2 z%K>Eq3D}U0ARPYhnrl(2>a7w1a6=5F;*h3q_&na%7l_J8VU~aax$gi-{z-tTB;n4; zwkl=n7ThB!fX%9~+d(&9dtuXL&elVEa{6&c@b~OP?YZG31M2bP;oguGTnjHOJOUp* zCOX;fC9My-!~8~DFcBPgm<5&zlC8wMK;L}SU=7$B9UGf`*6gKU@wFuJoDJqhE7jH2 zVTnby+ML zFW>I?arIvxeBgAx=eEB)zNg0nPu4w8q51)s0xWg%I&F5E z-q_X8U}H-Y8ag}{8mei!Dpn)r8nht597uy$ql1b5=6xZ3Z<=}2MhkBBixue3vRnLr zh7pGM4ATj7g#*k4W>^L2*&38Axjb`*hDz3^J9G#crKG@}KPt&Y2{aWn7fY<+cp2NU z7Ll5S8#k283Z0Yxs9%dR+IA(ovQmpi^M$sFN&G-O(Y29l=O&RBn5x}i5Z*rX$mh}o zT){hg<{IXA21bX~k&kch%s(}pU-4F-GeNHY^ggqr?$0FuEM>W{ol|lBkaPdQKzV1UAao9r zT2bL|NCsjcHa?cE$&=%368iC|zPWde9!lCZ$~)}4XVCm7HTB>`*wKfH+tOd{i93>G z#c*H$#sxB}fbQZo{$j9_UDxBP-p`QEn+i0ntir1NuWM)q-ZO}b35_EJ(=nEjA<%7S z&;z=5X%Z=EQNT02UNQ&Z<@FpFylc<>lNJh_b!*Uw0;fBDI0C%$geClEA7r?t9F(?r zwbVVg+9P?^Muj=>tMk>&IY(?E#E~lsG}L;ovWaU0>+OP}5*8l5#dpRD;7b=Sp?0%_ z5H&gdrPT9Fqo2MZXl+^q%xO#Dq3qn;?P!vy@0)O|h_1$i)i<@V5hq6SBa@u%qXss~54n6;7_>*Df8;@S`TM)h zzs1pjb^Hrc$*kUdac1T(^=px;>JkAq4JGIj#P2}QS*F6%npP8{Cc3VswzeE-L$2=Z zV3x<*M2ok~FZS6DKG+T}95<4ML+E+27i z))nhZ@x4gCUF>|Tx%6QYjZITi+&=m}^5hFg}CPQA%bTK&%G&WHZK0!glV~?gk3N=wNH7K`PC9_f__4we9Hw)xq zZWZxXhfireV=k+5K-txh_@G_n^E@(l(LspXf{Z2kC2$pNwtT78nuiK5)4l!?>`B$w zkZ>-b#3odf9E938wx&p?<|(I6{hlAxC=Fr_h>I&QmlW&m&Utq;BUVGntgH?&7iFlZ z#qtWxI;c6TK>{vs6YZe7C71t}-Yz@Nv*wZ%+^L5!2Gv65$`74*Yt} z#hC&Kb30+J)nf?i^U|9R^`QEj!ETu^1T5i}Akkg8^UJ({7)1SQ+S;ZJ(n(u$S(21j7UsbMiK#vmfv8p`+Fx&ZP%}UXCcl701lI|Apd_l$`7gVopX; zF^9ShE<ed)N3}Z%xee~)Weq{$!XMbe5K03fTpHq)m(~MMlWzB+(nz? z#~n`9y?v{NC%ZE&Y-uJ+0dQlzs@-=x#e;0ZbP=A^jdqan!{4W;tKo1#pxEHCHg|u% zx!stl#hVpBe=)K>jf!>2jKusf%w&vQ+-<#Yn2!GjAgvI(l8>(e5%ds9bXRYV2mi>D zpnLr~n=CFMx{F9n-Gg;Xm2Ol}YdDzIJH~cPb;&J7k=v%XkxnPk)hSi<8*pvpz5|fq zAaO##7y_l?@NU_%oLuQnx2Jje zpUPYJXK60u6`LfiOvGNatgJ+1HPoz%*=riXL$i@AoD|d9suiJ0-Kuig&Ya z0@S$Qe@zj(Vf`oR(--tpSzH@4CS~P8tTdVjMszRTGjgaK^juJN8i-IXbNs|S^Mxgx z`V?xZAMsRAF9o83?lqVU{c*-0o}M@W>zVo5wQHcMDvz2)Ug+-L{c-8n8(U8SqtZ3V zdo87|t*7Zc8yu?^9*SRVf>JKcBy_m^Vp6E71wotUc-cGexZPc5b@B>Wk_x1CwU875gf z3}xX);vIms(m>;0G|WV53=hVtN%NF`O20LF_$2D3%fEt)s5W@xCo*&N_T3+~sbk`J znAH^?PP4D>dr+IkYqFCnHVJ`Qu1=z8w| zElYRl24B|EAB*dW1a@zpDOp01$?*>_{r%CUp5=0@+u6|_dG_#`f<4tzA*fEjUzfUCgfg4&$*TB$-pOc?qd@iUIJ4X~dJ8 z(R?c_E3?PQ9?l8`f_BQ`YW`v+KT6K(xlgaZ!ju<%8~DSg zkKvPhV`oTg0T|mMMjgThRsk;-SUW-)%z`9p;NIb(2CD|}-I6T^yL%$+x5d_@@%G*UURiB^h$Kbsll}BmaQIW4u76b4U zuxMWi(vkA>=$y|_eS|ph?B!z?o+2pl-zrh2a}V|ld?^Vr5|=c|c5VvrJc4Rf)hOMpT4o-m0pqoSd8ij~)R*;~j_N(v!fw^Z2%^5jg(} zkEj08VOhjY;*7sB%A8X+Fcm#$ns^`#ReKFN&Y|<%^ihhq6W%|*inwTfuPU>ak$6o{ zSwd(wZ6gyJ_}C*;X6Z&O5a71Exjn^v@@DH-OYb39xE*3nASBu)nARyri*qj_QQANd z^LM?N{h*~pBqghbCu(?3wm;ub2}2@Xg7^U#^x<{uxB@G+wzZTT%zNB6K4^D+Mo9+mRfV`If?2yNfW2>QJ^J42Ntg-D+YDN`r+T6|i^p|9TJ z5@7---oc<|&dsD*FC|4rW{wLSrZ&sUUPQV*CN`EPEsKA5p})eZ&w*rmZxJZ+VW_F~ ziQVe0PTVN{FT3%)7%6gW2c&G0f&FkWYwG)(;ol3#-`ijxN15G)avK*zrZtyxKZvTbcjTxy&%R&nzhp;(w~l_rTY-W{ZP{0efM*{Z)d$cG+~F& zt_S-e>iK`po!dlRtnS;L?@2j2KtMhyX#Fo>#$-)PGJ|BS4#C28II^I2X!Vbjf}hGe zE^QtXI|0XL8 z2$G>$D=6@4ulKii3IT;GU4u*!t5O$+d+DxTrRvl(NbtpOV)OsjK8&( z{$8$wNUz6eOAgC~FdR-xfLkg6EZPd=X#Yy+tIv(fLZANH{}U(}zI_xn258Dq|1B2} zq%X)cT~$@p%!L2nH;<3e8A#Fi_#iykGoSiX`NWCu_RmviN%k>s4<~)+SO$Y$>g@Yt zw*6v{AAdigK>0D!gnpqJ#X%4cYmZ6pNeD!Qu+dP<=ms&i$;Jj(uI<~mPcXB&=ItZZ z-*hOt4Ni#6FI^?ajFUgs!9iVzT7Kx@*yhsDmP#>hv^ouU4b$aZ-BFQTY9!Rs?H|8v z5I@thxoa@-g`$dXGad@e`3TUtF0`cV?5;%V9hPI+Cwe5qmJUNlCxf)a+5j3s2R!-p zVe`6k>Mr7(LUnkokD-K+WaOFRpDO_Etnqs>Zftj3Om-OL!Hm?ut-KuZ>lwIj^$;k4 zB#IsmyZ0R$H0JwPFeP?fJ^*X2*Q%8=?8w`{f(Mmzs%@);zqeuRi-nLBaTS%-h`KFi zXnA6-PP6|xY35I=k1dO}b*EUgmo z_7$T^o?dbB>7z)a2Uq8Kn3Mn@bauS-7ecD%6=EyF7^8%~IGSx9+=pqUor+$Q!WeBL z_i~;w<$kV53lDfr@l6?97UHZUir9teDZ-LUN`^DO;})N9mDJry zj-0TXfo{N$o`eAI?*{pRrId_JK(hAPXLUx|j8}d*pm3TLdTiifVIc7;Ncd}9U4P2# zS#F8)u?PDGhA0`*CbO=2&E`ZNF_dcbT##Blt7KbS(Ws%*3C-%q0KTO1-_0}3VggO7 z*EWq%2nq~m<>zbqEmZUp0Gx!uU(okHP}*2jM=HRbs-)}P!Gt+D3B7E(IaPyFAiGC4=d#Q z$@&k+aG*6Y+w4{(@y0cdm=)0TE0z|X6W;@0DS@?$%6$FQO|yY%T0KTSyEP6mDBvSY{Y=Ef;5XDMxrqkFB-z^&;m z3dp@3!$MRSVl`UOH_3BQ?0)hMj=r+@^!R0jN$iu1W$P^Z zYvZ71$mHe*Bqc2ZmLVAWYWWQXFJTnV%+BVH@2fM(vMyS`emy5QHyylr8ylNaoZy4Z zg$>&g`M0-KcyVK7-WCD|@)=_2Wd}VbO`t&DQqnxV@k0`Wfgx?m0}a({xL^)k@JaA| zlI2F~4MnGnq3vhHPvut`7#p|$_0oR^<(pWoP<4;fny`@+DEcoMb$NQ`i}|RN&F=4gm{U zGy_X>tVQ=d^TmTaYkax_axQ@_A@0JFsBL z5d>HpM2>=QOoI1v)jTU!SP>%whx?dgq*w1)7Q(fVWK1~@EL*-Dhs$vI{%kacMC`+_ zx~s7&X~V;4Oj{m3@C+?|4bh%6%&TARI-1ox@XGu4XTyJjo?5$hIsB)YK#JPNN!&D$ zd?xEx5$WHF6^5gK^2@BY9*35VD8hP#x1wM%5EIjRa^*qZ3Cb{U4AX%m2ZWr^(tEiz zLutFDGMZm_a?JDtNq-{ejKT8%PxFAtdcvUy2@kJ8AH)GaOR#uvY51EDD*Y#etmE!P zS-gpfNf$X^0%Hl=w{Hir`laX<@i5$zO8@MKU?Zq4UdH3*bjildWhs zI+z#rEH_u?g~|Ym@bTlrKICi|q;HT_``9&-v3K09qx-5AS@i< zuIgRsEzoQIUtkjn0o0%hoJl}V<=|KG(8tcn8l`fL&G%<kZ$3>8PvoeOm#wjgjpXz z=BHNX$(a>; zII+&l$A=Z{WQE#kYYg_R1vX)(0tfH->|MKlJ;Ex7=Vl$G5>8!}3xms|evG3v@pHg&!%S zJ)`duXqed3VqX@x11fd-KZ$_nJu1F|s89HC8VV=%S$(&&K$B`J^vn#}xeT^NEg(Jz z4x2)aD>^F+w-z-{(5-Qp0@N+dwgmD#F^jZ|{Lg40xC)Ff;_nyGWbW{AYc|9nLWNUR zmQ$Od2LU4i>lDx$kNRW1SfFEHfh@M?IS+eScg-$Zg z(m=}oxzF^*JN^r2*PCQ(LW(*MVdmDJM0Ek=x1c#{VsiJ*PKO%TI-0oo1JXPJ8Pbxz z5*=vQa_Tc(bR;}PhPyEXTXV^5pGn3Jm*wcBfSR-vz&{HD;NZns+9Sa@V}(PM?o*9< zE35wA8U4mG`YRNL)3!EX(NUOT!8tA>r!{5DO@VSTYzuNO5HQ<=!Qb=1;K$+`IsCsb z$#zcurH;!VMwpaDIs|$gR7mF zM2kNh%+|Q-(9rUUO_?-(X8u1EP3Y_| z5Kw?IGCWwvU4I*5GaSn80=U&<<#4ds_QM0b&9$|9mG=6@l4oWvy%zQX}*Eyq_H*v6{W z-DL8eq!|g;eHl+BgurGYC$-E#PXROgLJkNd!tMGN2_-Jbd4TqQb7Kma$L0Yfv+hVd zZzSL9+Lk|T6SaG1j5@wpi=26f(=&G=+O?Rs(!K_`Q9HohkA1v`oRb9pNhaBdHd9kW zJn8g@pDlxELqaUZHVzF0I5~qchXfOn!fFV`s|YpbnxgkrPD& zXy}O6ffp^`#2#y+uS`<}d`M<-p}3F+NT!^foU&UihxQEl`oomjM26kGzF841^e98y z>C?~&X|7$vBN`%)h)fUvYy1|9lHn5-~o^ zZtirhxpp!StI5J_p|BU(&f7kqB7xyw=-e8Q35XAHX zGScn(?;sHY-mMi1A;b`bKTO^h(M#d=VeH!`i0mhYGo4_N=$EUZSH3NP2XRni^k0rq z*=Ng#0#M`V=;*jsu1s_Ni_^Jqie4moIURK<%ltAVQfr);k`R3gg1|~;x3y$cg4f*Z zUPH6=zs>zTeypUSC5OjyOVODYvRO->BQMGZJi$XI*?U|*VR(l)tC(Zu!0~KARryjG zLTH3PDVff(Wa_!sxwTvNzkXb51MM|LzMKUXylBtj~ zR0<&(cH0ysNuiRNMW%`^Ln#^CQX(QURBD;02Bhb>s{6U`Jv{HbpZD|jPoKnct#zH( zdH#mu`2KD;0SX7V6rL!iWYGz5GsyUG=W{2kg!k_ceQ!h7zOX@DABD`5AYK&M!8$-c zKC|||r1Rcu!wKo>rKOfrunDPJEK(4E!5Lc=<4zY3HF1W?-XkCVptwffLJ*KGMPN;# z*uW3o(D?K;shs+Y_s1m%vU?@4Jh2XBb3eE}GB2?m>^d5Z6e-%_!6w}Xm}If%1|yf- zmc2%Hd8IuzWFg#3bt>=!8W|>Lb3V{XrCrn8v4mmu!j;o_zU=ESEGHk+GMbm5BHhir z;OXhGE+w))j`W6fu&jYf3%gKAUS1y3Kf$4}hK~A86wf~@uztjcPRnliPqM&1k%UK`qfOWBLy(5dpt5iTr~=o1Q?-XpeA$j99)`%Xp+x;T+!}W642MT~J2vMWMub zGHWMUI4gs|jqKMT_bk#}udM!+;r*<$^H=$ZZ7nAWlyTYA)zzD(@OTg;m;z0vU(r~2 z1!o|X?Z`5iupxLLeK}6AnSSI4p9A|@J=r}uqUWso875c{P^*St-?zh4Vo3Eb6)io- zKK`*QNRQETjKjJz=X7wx?0)VuP<$e1uu%4M2kPV&ZE|ff>Uh&b?al8qQ)Af3x;^Qn zA#rVpAjrfDMGQroGj)lIdkT1uI84baJK~~uU?-%`!jDcCRMOB>E#%Mh?GK; z#4#kDTs%BvlD^yf)?nIB5XE34B>4Gf($=hdYD5a(NboBHHFZ*|h*D2(!iG!JDB$Yo zbug}3j?`rb+N%)6g^{vcG$qMJfqJ6UhHc{*i_$F5* zK_(9wc1gemPMS0cQY%e5{1EbmHPXD!o()p}#*pM2ih8~(h+=hQo=1=XVLWPNJs+*U zm8*v7E(hUEG3S- zOwQERD~)p`$H?Z*0nV*OqOjqBAsgJS>9$bQ=*s-$Q{engMm3e>g;;)a)^Hk}yM2p} zl}%RdJYE9a(v8PS87m+jdp|i78 zlk!OXi%b@otrc`_(+SDI(3&p`IvnwYpfno%ZaozOz#s>aeWAYk?JpaWqDAQW#h7c3)bXnI>iz6trwg0>PlIhz?eQ@xV8g8q;e zS?t0(a=?~QdSVcU(R%H}MJ4&`u4XF6hNxc`1-})OSd^qfq63OPJcndpUvV{SRhgh-XP7Du7!g4-nOFt|d#7|E;P{ zTDgA?0z-ml}8r!-t-u-3}riL*LzG@$LF1g~E3;0ZdmZt`7PWywk{?@M%kKQ{S+;sKv zm_yMf)d;*Ja=o^xynw+CB6#EZMA9UJ0EPwF5K>sz**P20tjn9L`}G)(2|m7<9(l^r z283`EIC2oiZkf@c`dh}rnOW$5)$i09Ccw%$qX+tjO}YN;^P*X)Wb%>5Q8Wjb z2vUX*Yc%&-zCBgAGg~BsB*3-i16G&iM``{LYtx5@4>BF2FW7bT=!~w}7i}_o^2hKZ z`>`Uq*B@?4w~9;~4vE_C!F#k)k&lq~opdC#mSVmz>VdEF4I(^MR_0cpKq4cBxDN2k zCvsIyJL<8O$mYCMJ}vlpbW6|3isFARamp@^R2*STkRVcs(PXco!Hp*=4TTVgxrTfz z7``nWvb{T!d{Z)#K}_Z!W$x|W(A!GgL9S0S>xG80qqFm@Na`9>vqJW4wvw>LeKzVQ z#$}^$-1v%eaZw1k&;Cy!tMKKQ?q5it^hy!zP)n>E?s9HmOR(BylA1)zkiZ&H-nD4E zQ=kG>;ScbT#u2&KBEEDQM+v=~cK1aKDnD9{Fv^~&J5O<1NE*5nU;m4{GL^RB&`r;p zJX4_Mow}k_3BB8umAh7lCi|WcUwbYh?4GJ$k*FBwO)JKZPP&;P*Y?@FWw!z>Y3ZHb z?|H@uL7gwTpWTQR#A-^#FHG#VYukGZB}XQTkTPqWdP61b8@}C(CgguzVa%>&F(OY4 zcQQ_oHJ2p531dm7TpBxXN?FDxE~2r8$OXub)gU3YyLaPJj+>SbavSR&AZeu^VOz== zBu1(A^yGQQvulHliB@XAlAkaF83O?x4f@rAUZSk_qCs(l%Fz7X z7VU6$qo%KuY!|LrOsMg>#hPBqw@!0CK-kv5w=k4MNlOTKc-I2$)#9q1M2_h61EwrL zDAlW?MYaT;o(t-=pV=gKV^aUV;!%#_o~C>+g@AYOXbMY``&aH-67=@5@A5Rcn=9F)a7I9Fu zm&QAnmKO0Ly+>1WM7(uY_Tf>pzX_zl#gekgH^{zMGol?=$B;(=KZfRP5R| z=d~*MwSRX>MEzql)yDc=3O|I3*pWwbEO?Lh_iP_jti z!{*_8r#4HM$NrU)7v1O8+k84I4>!8Lyk&pYfLAUc$=YeAy4;%@tFh-^mE`m4b@p6w zc@yh5JAXQ8=6{+ZCDlEryi>4&{7wV2nQEf^X~Q%^Fz$+n1y_G#;&JK|GP1OZjpvSac2EtelLVoqXIFs~$8{?da#jJkGL=#@hc()<2H*eV0Y0 z-12SRZ&wWFH#^j|^4MFfS|4*FDM5v5TQjs}^!@@}Qh?y9=dL+56|5WE zI+c4~AnnNJM(>pJvYc4;J#(8CyiDA;Oh+gNX=kT4q)8d}$iJxWxy)$XxmV9?ffa>4JOiMwoI`l$7C zglwKj{-MSzCkBHmPdz@Fvf;~$Jx-G>z0jAD83A%vCa@0pw_IbDj;jh2Gp{f{&+dO^ z9p{j!>_e6t5u*LVCta|za|B{X>DEb0lOhr5y`W3rgnJ!LuEpTEaBtbT_P8DM9k)uO z=b2TrGn`|dGD|zzRoF@*?i4w0Hyj;V8FMdTTBHiOBU%1C=HU^HA|g6vB*wX@S|+QE z{iI)+9}#j%n+TXb-FiFH#D5YJ;OK6n3 zM@-TrAb|F$G2WG4Hn*LNAz72Tw~==yyKt@!MJYV9i) z{t!!jqLNG;8@l}QgoCKKiCN|O?J4=~6s#)Kgq+I}JPglQCydXS8GpPPNnQC+t8Ep~ zApg{=H#~gPt4>Z;Rpd$ZI)#1O5Uis5RV-SZqB0a@{wSU~yztm{oj^}R3A@IoJnsSO z+6>o_u?`C63A}l0<}d6T7SR~%NoSex_q8Abjh+E$G(d~n2^$w#-vdkT&rq1%dKUDr zOFiDITO{h7UbySg#&pXzOy|C>AJxABU_#pIqxbWYcG((<=83I49qrzgbIPC*JJW98 zTOy2rC7?*ef(lXI$WPq{g``aV4D%o)8Asi~;zyYGFNu0_5(9^T$%IO>S%S(;-=|u6 z-;G0F;nQQ!bl-Fp)8G3hs&qnIwd|gI*Cda)dfDWKrIOnDZh7PSSAt5FW@?Hq_3-R= zk9DWBw1M)V7WkQ5ubXjkyg)-N7T*0<)V()aB=?JQ)PAp^mNIRd?y8K)+fX39pHOsV z`aOvqUN;^om)DGLJ=Qk+${oT!4nAGBZi(!k>t7Z!4fk*^L#ZbHs#|%G$ltNsKG=CN zhhK79TpaxFU0M5_Qzb{t!lldSwstbkH>+3m*SwW`809;@4Dpe~X_V0ZC&Fl^ow0(3 z*6-SruaxZvjb=V!ro|W}qA>yRrIznk+`pJVJZ}G2`IrEmGBzb3E3U}dz1V83udj(& z1E>b}zIzvn3=TzXvTF|HiMD~kVidIGLGh$TqIiW#!`+0*Y?g3j-$B zix&$qAtbq`1l6X*>0H{8gmtwQE=)sV5!0G2EWPNC^;u+!6?q0}|6htjZ+f3mBoY!j z>D4`{=d>rU9+fdN650q;i&?>(t6QLB_?uo`x$&rpb%_OX_Rh@j6GI}gpS7*{z(6uJ zmdNBXFd?e_>GS8dcuA4aTUF>uElbKu4NS!)%L?V4{Iu@ErAPU;5}Lj(Y+D^FBE9F8 z2Ip3^Fu#03B&s-AiX@lio?hl~ohwFo>l5+yRi)-ltGB$I+WeYWp`G1zsi#u;wb--R z3)3PRIo4*y!Fg5!A7@+@tT1*;Yu3VVxsrdW3N<&iS}St>s~ykH)cR?x!(Ggl`}B`; zXi7Ld*b<~*@r;HZ2<`NSgUdb^D_ceD_?MY;9rt4FIaM%kvyqK$pfu9lO3mcijyLMx z2=)2#!r>6t5T3Sc+p}}R&wDmDHHDy-igvBNvokWxwvX+08gm~tbXhK3Af`V!#J4MZ z8+@`3bXp}j$sw*P3T0DG_`deDVi{fE7KGyEw|$-O-{B>+oG-(q^7nmaJT)lW`&oZK zNlp@myK>E|t{%;~A2_Dk`+j%zO&)8j!1T!VZ30{k*YnGrGP=;GTy3NBwd?2(*2zE= zNn`rfQ#S6t;h&m+L-`~JdA7xu-MBHEv(HYJh`%~yn-!ln6CTz4G=)}h>eE>P#>YFu zT|hkG@#<$l8S2Z(tThvD)W&}5;LN9zlFm6LtLXaAm)1otJU8x_v_NoKKaQSIa?6Lk zGLz|#HR7({>Vu0VP^jRQ;g!pjTW~TSiI7t6#D1c`erk+yV%-QOAFO^{856u@4+K)&@a$V z4=XNKg!AJVl&y7sc`)~)d||(%LOvf6><=SlKADxrx^u-ssp*^iPt8&WPpz!0bg3P*cdB zK1X(jFsBVNk1Al~lYi?twsH;6{Wz1iYEhUKQXAwPE^`Eo!Kyb*W~GMuW6$8 z4{^(?A&5U*@mZqKV+G_kh5o)X0hxtM!q1unsyl@GeUZ%nyzO{9W+7yo=D3K72?#)L zvFN)2F3ph6^;Cv9GjsA0o0;w!lkav$+pXLJU?sP_Z|5v5Bo^lPsIs^Xz2FtgGF7`4 zKQn!}muBX5{YkoRv5?%e0W8k{bhDGa6eeFoG-@o{(hJSC+C)5KF6{c+kf1*}m-{}y z|N0hENjPecPl#`*?ub1^18knA>zdv{6i-V_44W}SOM@OsCH7#QjU-dVQl}EYp zE}UiYo5%V-#j$fxpXhCSo=JVYBYv8zO%!I(%lvkxfqB^+g}3(3wHFNf-FMtdVxtlh za|9Z${y^T4XexsADWX4Msi|0w}qfi>vfWBq~79WO);OE1Avm zkCvL1m|R$g>+SOo)T)J7>G0upjzcfGSId~Rf4xo7?S0;SJ_7CwOEW&JYk}o!O|N{&spy6!)xs5r)R+`sLcJo>o-63sY61?2G5VS zEP3*2=StD@beYHfX8e@o+OOEr&CsY7CZ+YDrugW*as!Ob&;1EA7eSLHXf5>*#zNvg04RgMN{s@C6eL0F| z=%LpQr}`L^cd&^|W%b|Ky}EVJqs0ObOMa=hgyME>Tf*qu8-cCoKG(+-^Ye2FM7M15 z&45xsLBUh(Tj>xpPfTASTl}44W&9F3(j`J#Enx2a>0hSB9IK_=SEcSO>K4B!DjAfX zam!0Xxtop4?AH;d806CF$2|>asy^6;>+qAW@NjpPY0BA5zg|x{EmHyVE70F~`g!Is zOQhx$;x*v-;z20zeWW`e@ zMNYRMfWqQc=9YL4e;_la;1yM8Zz>Vds@>*-&V@9?9;3h;7eGhBM!pLqEFd6@s->lX z%z$yV6e5T3WZoVCKKuvEQ@-lQFo(tTn8P=%U)6=I|6OE$GR3>Rmr^vL;^Mw)&!LSBi0F!pHC9 zF61SHwD3m~jh-lUqLxGCiOTJPFq2i%HLUWsnz2?D$0dpbPu03wMp@h=QPAZ0-f@n9 zIy-|vQR+$>jwzCt0^qING@E1A-_Uc+pra9mL@F3w=|~a149uH2kA7wvkXG+s-Q578 zZVdl653bP{Zy<(D0)f#8z(0x781vVVN9n}FxT+=h!-d~ zB1FtHNXb3UZw&QY1nofqdKGJqYnjb~w;KX4!hW=N@vf=pqAeoF7Qkn7Z0emMx*&T| ztmogHtpHd<2->%cAP$Je9hQQB${aID__nNxt@cjyF6@5FSt9fnPYs3miED*nJPooX zavaQ-mDdBl&RCGpDKPa20$@@E{GV>UfD-y4OC=u!S!kQ&=G>c?d;*By=ZRy>AwC$r0o?L+@(5)Y-juAAt2RLvUlA z8ZVpCG#31ZB@B~cs_!TOI6x;>)=2TYZN5{~ZD0PtTNBJ*78rPgAQ}cWNcNT352*;~ zgMF|Nkj8dMMGy^ctiy)hpWvJ%{RuGu)88`J9q&wlo6-xb9|=&2aywHjtG=UeWHW6PFK^FJFG()3!ZLn14C^GOf6& zt81SR=0++u16PD9$QsPovKYDsZW=cU<87J(G=~MYrH!3F#@AI=!8c+j+k>wOxK|(+ zT7qdC>wrJlPTC&H!~+(lu0sR74BLF*aPJ)&l68A|YloZ%ZBl9C##Ubw2vf2Tej^7@ zlB#5;b9-nVM+&>;Ju7~x^44_mVcymMx6GfrR)gf&I6!?!4ltri=wlbd!nUE)hnxia z7x16^oe41cNE;X_BfPw3?=PSd{(epQIeBRm5F9jtFO)y{`?52SsQO<0p8)WL28o;z zikT5(ATQQ-F^na6@eNf|gG5gmo z{MZb40Yp`Q1qnHl|8>;8WoLzyagr9~myoQmp&?AJ0HEJP7yWjP_UI_m1Bg37W#vSS zhPfYtXqKAFPBbufv6l!r6?MxS)~Yg`b@>U4_s9NPg6BdvD(X}#R?s7k6g^eH=^&En z9;L@Fy0yc;fyqp)>s%)mrFzGaLr5qhPW&~vlGu?UG@!VKS~ItrS|`VAGI#P|Md+$x zAF)+4pKG85=!bX$gT6vA<*aEFx=Q%lbLfTPqk5TIA9j702{k?1gnmft!gYay{km&= zcIv7I6cw^r(ji9rUrZ};u86Xj{{A~Nw!3_L5_M(5T$?r_w5lj<`JOgZ4H=2IqL{In z=z!T}Cmu9483O&flynP1wza{w)^+KBT!-91UR3MAW^LAO@{?`lMY1!HKKz-|cIKT70=RD7O`NPZkUt8}_ zyyF~|@WRuTg<2EkL-iJ&!5Uv&ZdAwXELw9}q0v}qrF^i;s4gzoo6JT_v6(hfUg$kY z`2c;1??@w6kQ$mMD5ACW2$d)V3ULk92zg&FX{X--bg!8JC# zlY*FOQQbZw@O>1UQ+z8CDn=7fKy%Ro)PQoR18qlp(E-$jPM~g-q~iUk7)?L{%|#1P z1InQev>ok52T&I}fx1&EOM(}&SXGHs*azC{EgxIJg~%*sjiU%YF6& z5bygKAY}6R3;!V0kLfu$#x4@QOo|z2dY*@ww&Jk2Q_P`0{+rw9fmJ4}mj$!Binz#( z2yfr7@;D$6G1@Lq_TE=3&jQxYm?=L2tWE3=xfWQPK!N-$u<~D>`*|Q0vx`5{uLCEo zTx?D228h@x{0L7$Y&S+L_l|)mVEKwN;S<=>#x9-=pM{~}-KAA?D=_(Z&!xQPAX%TV zN0&brF0)zYdC_33Y)HuuagkEl-=LEI6BKbRH54>0J8etMu^LRJY^?7q@KNG4B zt+t?)$<4Jb*Dy(ldRw*6?{<%MN$=V``{}Js_n6R;b@Lfhe0bXVhUt(rI=?V8>3!!M zGwRg?=Wk|6^lRrv7G&MRigPOqGJPb!EuID0aVPXf3JY>vez`pdh(oG9bL$k4Ok3X3 zPCbyQ3(ooe^FTsGOEaT(AdjlKC(CWX8Lm#PX7>Ozkm`l$=is=jm8_MyKoe576#QWV y9yS~c9M5)xvVKcTRL@WF2DQm#)u}SoCR16VUd4WQi<)L6&)n`xs;}>|#QyB^HY(9G;ZnUV@t^g=1j!n9B#qNp$tr4X44Vrkjg_I&7D=fnBkd(ZPc=kgbPulxBD z&pJ@8`c8Kj1$N(yN$JxXITBpLD=s*{S+VoQ~YbGK|LQP4V@_tZy}B+=~b>_h8pdA1B< zp@nt(iNyO$Y)|$+$xu0(fC8F>7N90nK<#J;+J_FJR2K z0Ep*%G!Qa%;-zl@>ceywje!`IVRPUd-cwRJzY0@N_Fm3y2~hS+ zdrR}?!<07bf+!n`RXX!Urx2k~da{zbvr_*jDB`k)X1PNi{J`xVVG>QtL=SWP%jT2; zCR83?Yegwjm~UUHVUl2OTea684Q}fa-?zKZ)7qNvGodr{)^n!($c*DH(;n00_{vn% z2ab7WK!RnIG;d)r&JPz<$pVv#uWpU9f&R z@Z%&rYCP^gk>!H?`Yo-Iy}!T{RFJuyY%hX^E)Dv#3U{)y++%B`d(&CVnR#IC!cRRI@v2CG!6oGlo^NlDpo zi7S%|3;hzz>7l1sqLi3QM8rIzj%Xn|iEd&Sv5)8_P7vpb0V1U!=M$yGR3akg5p_fh z(Mfa@yNG>6FL8o6PYft9G{K1(mem?ry#bqqCYtZo6!CAflD}OQc4sBWBZ}%#4}OjV z^M=^xQD9f~f@&T1M9ZU(R*2t0=Tz%yKY!L_@AM1awjN18uX0j6%C9Gu4@`sJoyRtp zL7i*g<}a}C+(lsw>x9?s-y2t5> zQz^euYRBPwZd*)7duKvPmi}NyuHeYV*zvTCU{@l(tL*<3^|U$mjSns&U3~TKHRwK- zxz^3U+ix#+fX=RO;T~{)xbZ+8kp-z$S^RZ=y=D*R0KG#|Z7zO&Mr7o3ft6cYzR22| zM$I5NJcKkO;H-MT<}0`=i`M*5K>Q)C*-$|I#+o_;LHz!>dM|?bXp{O7qH5RWKa1rE z;v(PG3y7NatLlr25Vh**ohc|o&KiHeN9jObPk6pa?n3ilRz@Dgb)Z1Kd~V=l4|22P zlhx8eWN8!>9nJNjwek}mo|xy*r@X1$v_Jp_i;`F!mD+5^YAw^7wbl9xJA==|1%tyb F`vY$gJ;wk5 diff --git a/example/testing_folder/outputs_qpact_misfit_True.pickle b/example/testing_folder/outputs_qpact_misfit_True.pickle deleted file mode 100644 index 886a4530e25ec4f233f20adea3404316d547acb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1042 zcmajbZ%7ki9Ki82J=6-+7Z&!ygis80O*0F#d}ge~p-EWAWa!*ZXXv(Wn>o!qs%7el z2R>>c)iBH#^};CAWW_X1?H`gESY#_uR3?r@R#DpT<`eX;dvTvT?)g1;_vHdw0|O}E zQG;W7f5vFSe7N3HZnR1?29}YOTguF(YL-=7F~7BdWi$r8$;?_bX54~#+P4t%fqL!6 zk`}C>Z1{*@5w620aIC^l5oNC<98nqRco;>Ko=;s)dl}sHtcV!+wx>ZD55BS+jQIy z*u$>afBiWq-mid#566lJL8(&v;62DmK508aN;NQ!L0BL044mldD4PQ3-)0@11CWa}WPf?4zrSe&O~`+buQW`d`4>rp4YpZi-m|+d zy?Pa;ymJ~tN@>}X?i&A($HQeezv3n?FIqA)xo#Yd3^+Ru3mEMI#u``nd!l zJvVnXF3S;BX;{1a4MIA+C5zt~M9s`?R?-!SGGtZ-L|#E$UT3~7yA3&)Cnpo{-9fIZ z(Bv3pAMzZ$+|P1j$W_0#J&*c;a{EI{WEG3(O=Iqs+&VAvg$grS%L_6Xlc`j#FQ`zL SSr~lGF_fxksExg0k^BMatx#?N diff --git a/hippylibX/test/testing_suite_file2.py b/hippylibX/test/testing_suite_file2.py deleted file mode 100644 index 15281985..00000000 --- a/hippylibX/test/testing_suite_file2.py +++ /dev/null @@ -1,114 +0,0 @@ -import unittest -import sys -import os -import numpy as np -from mpi4py import MPI - - -sys.path.append(os.path.abspath('../..')) - -import hippylibX as hpx -sys.path.append(os.path.abspath('../../example')) - - -from example import poisson_example, sfsi_toy_gaussian - -def data_parser(data): - eps = data["eps"] - err_grad = data['err_grad'] - err_H = data['err_H'] - sym_Hessian_value = data['sym_Hessian_value'] - - slope_grad_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_grad[20:30]), 1) - slope_grad = slope_grad_coeffs[0] - - slope_H_coeffs = np.polyfit(np.log(eps[20:30]), np.log(err_H[20:30]), 1) - slope_H = slope_H_coeffs[0] - - return sym_Hessian_value, slope_grad, slope_H - - -class Test_runner: - def __init__(self): - self.result = unittest.TestResult() - - def run_tests(self): - test_suite = unittest.TestLoader().loadTestsFromTestCase(Testing_Execution) - test_suite.run(self.result) - - return 1 if self.result.wasSuccessful() else 0 - - -class Testing_Execution(unittest.TestCase): - - def test_qpact_execution(self): - pwd = os.getcwd() - nx = 64 - ny = 64 - noise_variance = 1e-6 - prior_param = {"gamma": 0.05, "delta": 1.} - os.chdir("../../example") - out = sfsi_toy_gaussian.run_inversion(nx, ny, noise_variance, prior_param) - os.chdir(pwd) - - #convergence of optimizer - self.assertEqual(out['optimizer_results']['optimizer'],True,"Did not converge") - - # misfit = True, slope and symmmetric nature of Hessian - sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_True']) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "qpact misfit True: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="qpact misfit True: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="qpact misfit True: FD Hessian check slope is not close to 1") - - # misfit = False, slope and symmmetric nature of Hessian - sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_False']) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "qpact misfit True: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="qpact misfit True: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="qpact misfit True: FD Hessian check slope is not close to 1") - - def test_poisson_execution(self): - nx = 64 - ny = 64 - noise_variance = 1e-4 - prior_param = {"gamma": 0.1, "delta": 1.} - out = poisson_example.run_inversion(nx, ny, noise_variance, prior_param) - - #convergence of optimizer - self.assertEqual(out['optimizer_results']['optimizer'],True,"Did not converge") - - # misfit = True, slope and symmmetric nature of Hessian - sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_True']) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "poisson misfit True: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="poisson misfit True: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="poisson misfit True: FD Hessian check slope is not close to 1") - - # misfit = False, slope and symmmetric nature of Hessian - sym_Hessian_value, slope_grad, slope_H = data_parser(out['data_misfit_False']) - print(slope_grad,slope_H) - self.assertLessEqual(np.abs(sym_Hessian_value), 1e-10, "poisson misfit True: Symmetric Hessian check value is greater than 1e-10") - self.assertAlmostEqual(slope_grad, 1, delta=1e-1, msg="poisson misfit True: FD Gradient check slope is not close to 1") - self.assertAlmostEqual(slope_H, 1, delta=1e-1, msg="poisson misfit True: FD Hessian check slope is not close to 1") - - pass - - -if __name__ == "__main__": - test_suite = unittest.defaultTestLoader.discover('.','testing_suite_file2.py') - test_runner = unittest.TextTestRunner(resultclass=unittest.TextTestResult) - result = test_runner.run(test_suite) - sys.exit(not result.wasSuccessful()) - - - - - - - - - - - - - - - diff --git a/misc/mem_usage.py b/misc/mem_usage.py deleted file mode 100644 index c1eea8ee..00000000 --- a/misc/mem_usage.py +++ /dev/null @@ -1,60 +0,0 @@ -##Don't push this to github - -import matplotlib.pyplot as plt - -import time -import os -import psutil - -import poisson_example_copy - -def elapsed_since(start): - return time.strftime("%H:%M:%S", time.gmtime(time.time() - start)) - - -def get_process_memory(): - process = psutil.Process(os.getpid()) - mem_info = process.memory_full_info() - return mem_info.uss - - - -def profile(func): - def wrapper(*args, **kwargs): - mem_before = get_process_memory() - start = time.time() - result = func(*args, **kwargs) - elapsed_time = elapsed_since(start) - mem_after = get_process_memory() - return (mem_after - mem_before)/1e6 - return wrapper - -nx = 64 -ny = 64 -noise_variance = 1e-4 -prior_param = {"gamma": 0.1, "delta": 1.} - -mem_usage = [] - - -value = profile(poisson_example_copy.run_inversion) - -num_calls = 100 - -for _ in range(num_calls): - mem_usage.append(value(nx, ny, noise_variance, prior_param)) - -plt.plot(mem_usage) - - -# # print(mem_usage) -plt.xlabel('Iteration') -plt.ylabel('Memory Usage (MB)') -plt.title('Memory Usage Over Iterations for poisson example') -plt.savefig("memory_usage_plot_poisson.png") -plt.show() - - - - -# print(value.wrapper) \ No newline at end of file diff --git a/misc/memory_usage_plot_poisson.png b/misc/memory_usage_plot_poisson.png deleted file mode 100644 index f621b97a8cbb340f23e60f62e62a2b27a36ccfca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25295 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

Wl;>1LaUetcX6TS~SvSheL0>Y@{y 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 ufl.form.Form: - return ufl.exp(m) * ufl.inner(ufl.grad(u), ufl.grad(p))*self.dx + \ - self.alpha * ufl.inner(u,p)*self.ds - self.f*p*self.dx - - -class PoissonMisfitForm: - def __init__(self, d : float, sigma2 : float): - self.d = d - self.sigma2 = sigma2 - self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) - - def __call__(self, u : dlx.fem.Function, m: dlx.fem.Function) -> ufl.form.Form: - return .5/self.sigma2*ufl.inner(u - self.d, u - self.d)*self.dx - -@profile -def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: - sep = "\n"+"#"*80+"\n" - - comm = MPI.COMM_WORLD - rank = comm.rank - nproc = comm.size - - msh = dlx.mesh.create_unit_square(comm, nx, ny) - - Vh_phi = dlx.fem.FunctionSpace(msh, ("CG", 2)) - Vh_m = dlx.fem.FunctionSpace(msh, ("CG", 1)) - Vh = [Vh_phi, Vh_m, Vh_phi] - - ndofs = [Vh_phi.dofmap.index_map.size_global * Vh_phi.dofmap.index_map_bs, Vh_m.dofmap.index_map.size_global * Vh_m.dofmap.index_map_bs ] - master_print (comm, sep, "Set up the mesh and finite element spaces", sep) - master_print (comm, "Number of dofs: STATE={0}, PARAMETER={1}".format(*ndofs) ) - - # FORWARD MODEL - alpha = 100. - f = 1. - pde_handler = Poisson_Approximation(alpha, f) - pde = hpx.PDEVariationalProblem(Vh, pde_handler, [], [], is_fwd_linear=True) - - # GROUND TRUTH - m_true = dlx.fem.Function(Vh_m) - - m_true.interpolate(lambda x: np.log(2 + 7*( ( (x[0] - 0.5)**2 + (x[1] - 0.5)**2)**0.5 > 0.2)) ) - m_true.x.scatter_forward() - - m_true = m_true.x - - u_true = pde.generate_state() - - x_true = [u_true, m_true, None] - - pde.solveFwd(u_true,x_true) - - xfun = [dlx.fem.Function(Vhi) for Vhi in Vh] - - # LIKELIHOOD - hpx.updateFromVector(xfun[hpx.STATE],u_true) - u_fun_true = xfun[hpx.STATE] - - hpx.updateFromVector(xfun[hpx.PARAMETER],m_true) - m_fun_true = xfun[hpx.PARAMETER] - - d = dlx.fem.Function(Vh[hpx.STATE]) - d.x.array[:] = u_true.array[:] - hpx.parRandom.normal_perturb(np.sqrt(noise_variance),d.x) - d.x.scatter_forward() - - misfit_form = PoissonMisfitForm(d,noise_variance) - misfit = hpx.NonGaussianContinuousMisfit(msh, Vh, misfit_form) - - prior_mean = dlx.fem.Function(Vh_m) - prior_mean.x.array[:] = 0.01 - prior_mean = prior_mean.x - - prior = hpx.BiLaplacianPrior(Vh_m,prior_param["gamma"],prior_param["delta"],mean = prior_mean) - model = hpx.Model(pde, prior, misfit) - - noise = prior.generate_parameter("noise") - m0 = prior.generate_parameter(0) - hpx.parRandom.normal(1.,noise) - prior.sample(noise,m0) - - - # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - # hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - - eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) - - - - # if(rank == 0): - # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - # if(comm.size == 1): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_True.pickle','wb') as f: - # pickle.dump(data,f) - - # if(comm.size == 4): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_True.pickle','wb') as f: - # pickle.dump(data,f) - - - # eps, err_grad, err_H,rel_symm_error = hpx.modelVerify(comm,model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - - # if(rank == 0): - # data = {"eps":eps,"err_grad":err_grad, "err_H": err_H, "sym_Hessian_value":rel_symm_error} - - # if(comm.size == 1): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_1_proc_misfit_False.pickle','wb') as f: - # pickle.dump(data,f) - - # if(comm.size == 4): - # os.makedirs('../hippylibX/test',exist_ok=True) - # with open('../hippylibX/test/outputs_poisson_4_proc_misfit_False.pickle','wb') as f: - # pickle.dump(data,f) - - - - # if(rank == 0): - # print(err_grad,'\n') - # print(err_H) - # plt.show() - - # # ####################################### - - # prior_mean_copy = prior.generate_parameter(0) - # prior_mean_copy.array[:] = prior_mean.array[:] - - # x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] - - # if rank == 0: - # print( sep, "Find the MAP point", sep) - - # parameters = hpx.ReducedSpaceNewtonCG_ParameterList() - # parameters["rel_tolerance"] = 1e-6 - # parameters["abs_tolerance"] = 1e-9 - # parameters["max_iter"] = 500 - # parameters["cg_coarse_tolerance"] = 5e-1 - # parameters["globalization"] = "LS" - # parameters["GN_iter"] = 20 - # if rank != 0: - # parameters["print_level"] = -1 - - # solver = hpx.ReducedSpaceNewtonCG(model, parameters) - - # x = solver.solve(x) - - # if solver.converged: - # master_print(comm, "\nConverged in ", solver.it, " iterations.") - # else: - # master_print(comm, "\nNot Converged") - - # master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) - # master_print (comm, "Final gradient norm: ", solver.final_grad_norm) - # master_print (comm, "Final cost: ", solver.final_cost) - - ####################################### - - - - -if __name__ == "__main__": - nx = 64 - ny = 64 - noise_variance = 1e-4 - prior_param = {"gamma": 0.1, "delta": 1.} - run_inversion(nx, ny, noise_variance, prior_param) - - - diff --git a/misc/random_testing.py b/misc/random_testing.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misc/reconstr_m_pact_np4_X.h5 b/misc/reconstr_m_pact_np4_X.h5 deleted file mode 100644 index 712cb7ef0506c8a3479f6c58c3e5135c479a88ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239768 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%0DbI_Kz*WUVjy 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%0Awj8#)emj=y;u(szj;hj`mH zx_s%)Ro`nLPx`Q1;x%urcVD{i`h!D#Z{EbwujXy{adCnlJXp(f-}v{-S6qJe zQRn8wdjFiqmA)SkT_4q1f0~~IJ`YHZFIgc0lyZ7MAQ-1Q7&mr;22M_vv zX!iZi&<`uU>nzUU@p*^79}yiF^zN^|>(`MvcT~Ri@u0u@ufIpfPaonzeD&1#V{*RN z>UnJT^-;a`OJ9zQuluL|`Yz7#@zcAW;)^G~{7=YvJe-)deCQ`dm$!GF9DRtBQ?ief zQ?tei9Y3dKA3yD<^Xrpy&6WF4PuwA{=s3a689Ar#&6R!nnemI$yq%T(=B@R9mpJZ| zPxldr`em(O%^`m1`g?ZrY##8V@3?9{aVS2Y^XF9j=1~6fIX8ayo|iT5+9&@-^Uq(} zk1KrOkd7z3c@O{jxgS^hj-y`V0Vn!+LE_MzyD+-G}_@Q51`XyO=hdAyPN8NF9 zY0jyC>o~bAKK*T7{qfU%*GF7jo_JT}tKQ|awp`8Pk;#wY*l zvK9wFIJrLibp4?BdvSF`&WS_E+l|@B3x4o~pPS;-cl*xq-5lR7`Qi#Ec*7Om+E3@} z_jW$w-tu`8y8YI)A!+oZR9qi@#D$yXA`dHH-b`Vc3*?;bpSE$5%imyQ$t_1>rAqpRD~(YxQzMAt9< z*01ib{jcYoe(M)M{n_|%;6Cy3fCK&iM$YNKefRRYm#^QeFC%fHZa8T!#G&i+HH|90;8uGX6eeR(eDp3hgm^-F)hQ~UbTedqsf z{CE)Gz2d(RpML9e^B{jZ|BE>*d-s2g29_2b7m*F1FJ^_Q;C`e?2G-FNr!cOU89NBRFG zdFs2qsQ*v1uMXYcpJiVk)lD4d^|$@>?)xi=t6%#4^XT1o`{L+t$Dxb!i^LnL@2|$E zZuH03t zP~U%(d-UCX`t(-zf19tq>X*LLe;42H^VMJRtet;*Aig*_eJZ#L*{x*ROwMpZ?FR z^QA3Kx_7I8`#XO3YnsHN ztFt`RyZbe5&UL?9cfR}8`Rk+l=$ks~({#C4o%L(_=riQ2FZR`+uKwbcU?#EF5>HPC1PS=0F=;|-O z`AgR?e(%+nuFnEFPw)EMZ+*d>Q~$1W>zz;6O&`>`-$x&+v%0D0Ldj2D`ohuq)p?QV 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 z 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 P$Mw>Fhi~w+r;+~wIhsad diff --git a/misc/reconstr_m_pact_np4_X.xdmf b/misc/reconstr_m_pact_np4_X.xdmf deleted file mode 100644 index 78e418f4..00000000 --- a/misc/reconstr_m_pact_np4_X.xdmf +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - reconstr_m_pact_np4_X.h5:/Mesh/mesh/topology - - - reconstr_m_pact_np4_X.h5:/Mesh/mesh/geometry - - - - - - - - - diff --git a/misc/result_using_4_proc_pact_v3.png b/misc/result_using_4_proc_pact_v3.png deleted file mode 100644 index 7e67e74730f2ef84d8129403c20b100ad6822c60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32729 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 diff --git a/misc/true_m_pact_np4_X.h5 b/misc/true_m_pact_np4_X.h5 deleted file mode 100644 index 33c94eba58d1d7984e22e754513be690f4773b34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239768 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%0DbI_Kz*WUVjy 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%0Awj8#)emj=y;u(szj;hj`mH zx_s%)Ro`nLPx`Q1;x%urcVD{i`h!D#Z{EbwujXy{adCnlJXp(f-}v{-S6qJe zQRn8wdjFiqmA)SkT_4q1f0~~IJ`YHZFIgc0lyZ7MAQ-1Q7&mr;22M_vv zX!iZi&<`uU>nzUU@p*^79}yiF^zN^|>(`MvcT~Ri@u0u@ufIpfPaonzeD&1#V{*RN z>UnJT^-;a`OJ9zQuluL|`Yz7#@zcAW;)^G~{7=YvJe-)deCQ`dm$!GF9DRtBQ?ief zQ?tei9Y3dKA3yD<^Xrpy&6WF4PuwA{=s3a689Ar#&6R!nnemI$yq%T(=B@R9mpJZ| zPxldr`em(O%^`m1`g?ZrY##8V@3?9{aVS2Y^XF9j=1~6fIX8ayo|iT5+9&@-^Uq(} zk1KrOkd7z3c@O{jxgS^hj-y`V0Vn!+LE_MzyD+-G}_@Q51`XyO=hdAyPN8NF9 zY0jyC>o~bAKK*T7{qfU%*GF7jo_JT}tKQ|awp`8Pk;#wY*l zvK9wFIJrLibp4?BdvSF`&WS_E+l|@B3x4o~pPS;-cl*xq-5lR7`Qi#Ec*7Om+E3@} z_jW$w-tu`8y8YI)A!+oZR9qi@#D$yXA`dHH-b`Vc3*?;bpSE$5%imyQ$t_1>rAqpRD~(YxQzMAt9< z*01ib{jcYoe(M)M{n_|%;6Cy3fCK&iM$YNKefRRYm#^QeFC%fHZa8T!#G&i+HH|90;8uGX6eeR(eDp3hgm^-F)hQ~UbTedqsf z{CE)Gz2d(RpML9e^B{jZ|BE>*d-s2g29_2b7m*F1FJ^_Q;C`e?2G-FNr!cOU89NBRFG zdFs2qsQ*v1uMXYcpJiVk)lD4d^|$@>?)xi=t6%#4^XT1o`{L+t$Dxb!i^LnL@2|$E zZuH03t zP~U%(d-UCX`t(-zf19tq>X*LLe;42H^VMJRtet;*Aig*_eJZ#L*{x*ROwMpZ?FR z^QA3Kx_7I8`#XO3YnsHN ztFt`RyZbe5&UL?9cfR}8`Rk+l=$ks~({#C4o%L(_=riQ2FZR`+uKwbcU?#EF5>HPC1PS=0F=;|-O z`AgR?e(%+nuFnEFPw)EMZ+*d>Q~$1W>zz;6O&`>`-$x&+v%0D0Ldj2D`ohuq)p?QV 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 diff --git a/misc/true_m_pact_np4_X.xdmf b/misc/true_m_pact_np4_X.xdmf deleted file mode 100644 index 85c59ddb..00000000 --- a/misc/true_m_pact_np4_X.xdmf +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - true_m_pact_np4_X.h5:/Mesh/mesh/topology - - - true_m_pact_np4_X.h5:/Mesh/mesh/geometry - - - - - - - - - From 8cacc43cdc50179cf16fe08636b3bb9689dbfc75 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Mon, 18 Mar 2024 13:04:45 -0500 Subject: [PATCH 39/50] minor fixes, MPI hang for multiproc pact with destructor in PDEProblem --- example/poisson_example.py | 2 +- example/sfsi_toy_gaussian.py | 18 ++++++-------- hippylibX/algorithms/__init__.py | 1 - hippylibX/algorithms/linSolvers.py | 26 --------------------- hippylibX/modeling/PDEProblem.py | 35 +++++++++++++++++++++++++--- hippylibX/modeling/Regularization.py | 4 ---- hippylibX/modeling/prior.py | 4 ---- hippylibX/test/testing_suite_file.py | 7 +++--- 8 files changed, 43 insertions(+), 54 deletions(-) delete mode 100644 hippylibX/algorithms/linSolvers.py diff --git a/example/poisson_example.py b/example/poisson_example.py index 29fd190d..c30a391d 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -167,8 +167,8 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict noise_variance = 1e-4 prior_param = {"gamma": 0.1, "delta": 1.} run_inversion(nx, ny, noise_variance, prior_param) - comm = MPI.COMM_WORLD + comm = MPI.COMM_WORLD if(comm.rank == 0): plt.savefig("poisson_result_FD_Gradient_Hessian_Check") plt.show() diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index 0cb090b4..c55f01ed 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -54,14 +54,13 @@ def __init__(self, d : float, sigma2 : float): def __call__(self,u : dlx.fem.Function, m : dlx.fem.Function) -> ufl.form.Form: return .5/self.sigma2*ufl.inner(u*ufl.exp(m) -self.d, u*ufl.exp(m) -self.d)*self.dx -def run_inversion(mesh_path: str, nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: +def run_inversion(mesh_filename: str, nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: sep = "\n"+"#"*80+"\n" comm = MPI.COMM_WORLD rank = comm.rank - nproc = comm.size - # fname = 'meshes/circle.xdmf' - fname = f'{mesh_path}/circle.xdmf' + fname = mesh_filename + fid = dlx.io.XDMFFile(comm,fname,"r") msh = fid.read_mesh(name='mesh') @@ -127,13 +126,11 @@ def run_inversion(mesh_path: str, nx : int, ny : int, noise_variance : float, pr data_misfit_False = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) - # ####################################### prior_mean_copy = prior.generate_parameter(0) prior_mean_copy.array[:] = prior_mean.array[:] - x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] if rank == 0: @@ -169,7 +166,6 @@ def run_inversion(mesh_path: str, nx : int, ny : int, noise_variance : float, pr else: optimizer_results['optimizer'] = False - final_results = {"data_misfit_True":data_misfit_True, "data_misfit_False":data_misfit_False, "optimizer_results":optimizer_results} @@ -183,10 +179,10 @@ def run_inversion(mesh_path: str, nx : int, ny : int, noise_variance : float, pr nx = 64 ny = 64 noise_variance = 1e-6 - prior_param = {"gamma": 0.05, "delta": 1.} - mesh_path = 'meshes' - run_inversion(mesh_path, nx, ny, noise_variance, prior_param) - + prior_param = {"gamma": 0.1, "delta": 2.} + mesh_filename = './meshes/circle.xdmf' + run_inversion(mesh_filename, nx, ny, noise_variance, prior_param) + comm = MPI.COMM_WORLD if(comm.rank == 0): plt.savefig("qpact_result_FD_Gradient_Hessian_Check") diff --git a/hippylibX/algorithms/__init__.py b/hippylibX/algorithms/__init__.py index b2e218d3..fe72d8cf 100644 --- a/hippylibX/algorithms/__init__.py +++ b/hippylibX/algorithms/__init__.py @@ -1,4 +1,3 @@ from .linalg import * -from .linSolvers import _PETScLUSolver_set_operator, PETScLUSolver from .NewtonCG import * diff --git a/hippylibX/algorithms/linSolvers.py b/hippylibX/algorithms/linSolvers.py deleted file mode 100644 index 4a1b2930..00000000 --- a/hippylibX/algorithms/linSolvers.py +++ /dev/null @@ -1,26 +0,0 @@ -import petsc4py - - -#I don't use either of the following 2 functions for now, -# currently just using the following: in the _createLUSolver method in -# the PDEVariationalProblem class in modeling/PDEProblem.py file - -# def _createLUSolver(self): - # ksp = petsc4py.PETSc.KSP().create() - # return ksp - - -def _PETScLUSolver_set_operator(self, A): - if hasattr(A, 'mat'): - self.ksp().setOperators(A.mat()) - else: - self.ksp().setOperators(A.instance()) - - self.ksp.setTolerances(rtol=1e-9) - self.ksp.getPC().setType(petsc4py.PETSc.PC.Type.GAMG) - - -def PETScLUSolver(comm, method='default'): - if not hasattr(petsc4py.PETSc.PETScLUSolver, 'set_operator'): - petsc4py.PETSC.PETScLUSolver.set_operator = _PETScLUSolver_set_operator - return petsc4py.PETSc.PETScLUSolver(comm, method) \ No newline at end of file diff --git a/hippylibX/modeling/PDEProblem.py b/hippylibX/modeling/PDEProblem.py index 2391b546..96518a18 100644 --- a/hippylibX/modeling/PDEProblem.py +++ b/hippylibX/modeling/PDEProblem.py @@ -28,9 +28,9 @@ def __init__(self, Vh : list, varf_handler, bc=[], bc0=[], is_fwd_linear=False): self.Wuu = None self.Wmu = None - self.Wmm = None self.Wum = None - + self.Wmm = None + self.A = None self.At = None self.C = None @@ -44,6 +44,32 @@ def __init__(self, Vh : list, varf_handler, bc=[], bc0=[], is_fwd_linear=False): self.petsc_options = {"ksp_type": "preonly","pc_type": "lu","pc_factor_mat_solver_type":"mumps"} + # def __del__(self): + # self.solver.destroy() + # self.solver_fwd_inc.destroy() + # self.solver_adj_inc.destroy() + + # if(self.Wuu is not None): + # self.Wuu.destroy() + + # if(self.Wmu is not None): + # self.Wmu.destroy() + + # if(self.Wum is not None): + # self.Wum.destroy() + + # if(self.Wmm is not None): + # self.Wmm.destroy() + + # if(self.A is not None): + # self.A.destroy() + + # if(self.At is not None): + # self.At.destroy() + + # if(self.C is not None): + # self.C.destroy() + def generate_state(self) -> dlx.la.Vector: """ Return a vector in the shape of the state. """ @@ -240,13 +266,16 @@ def setLinearizationPoint(self,x : list, gauss_newton_approx) -> None: if gauss_newton_approx: if(self.Wuu is not None): self.Wuu.destroy() - self.Wuu = None if(self.Wmu is not None): self.Wmu.destroy() self.Wmu = None + if(self.Wum is not None): + self.Wum.destroy() + self.Wum = None + if(self.Wmm is not None): self.Wmm.destroy() self.Wmm = None diff --git a/hippylibX/modeling/Regularization.py b/hippylibX/modeling/Regularization.py index 8dda1263..9a3c62a6 100644 --- a/hippylibX/modeling/Regularization.py +++ b/hippylibX/modeling/Regularization.py @@ -2,7 +2,6 @@ import ufl import petsc4py from .variables import STATE, PARAMETER, ADJOINT -from ..algorithms.linSolvers import PETScLUSolver from ..utils.vector2function import vector2Function, updateFromVector from mpi4py import MPI import numpy as np @@ -12,7 +11,6 @@ def __init__(self, Vh, functional_handler, isQuadratic=False): self.Vh = Vh #Function space of the parameter. self.functional_handler = functional_handler #a function or a functor that takes as input m (as dl.Function) and evaluate the regularization functional self.isQuadratic = isQuadratic #Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian dependes on m - self.xfun = dlx.fem.Function(self.Vh) def cost(self,m): @@ -24,7 +22,6 @@ def cost(self,m): def grad(self, x : dlx.la.Vector, out: dlx.la.Vector) -> None: - updateFromVector(self.xfun, x) mfun = self.xfun L = dlx.fem.form(ufl.derivative(self.functional_handler(mfun),mfun,ufl.TestFunction(self.Vh))) @@ -34,7 +31,6 @@ def grad(self, x : dlx.la.Vector, out: dlx.la.Vector) -> None: tmp_out.destroy() def setLinearizationPoint(self, m, rel_tol=1e-12, max_iter=1000): - updateFromVector(self.xfun, m) mfun = self.xfun L = ufl.derivative(ufl.derivative(self.functional_handler(mfun),mfun), mfun) diff --git a/hippylibX/modeling/prior.py b/hippylibX/modeling/prior.py index c877b354..8f143ca4 100644 --- a/hippylibX/modeling/prior.py +++ b/hippylibX/modeling/prior.py @@ -128,10 +128,6 @@ def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean self.M.assemble() self.Msolver = self._createsolver() - if(self.petsc_options['pc_type'] == 'hypre'): - pc = self.Msolver.getPC() - pc.setHYPREType('boomeramg') - self.Msolver.setIterationNumber(max_iter) self.Msolver.setTolerances(rtol=rel_tol) self.Msolver.setErrorIfNotConverged(True) diff --git a/hippylibX/test/testing_suite_file.py b/hippylibX/test/testing_suite_file.py index 4127717f..f5d9659e 100644 --- a/hippylibX/test/testing_suite_file.py +++ b/hippylibX/test/testing_suite_file.py @@ -47,11 +47,10 @@ def test_qpact_execution(self): nx = 64 ny = 64 noise_variance = 1e-6 - prior_param = {"gamma": 0.05, "delta": 1.} - mesh_path = '../../example/meshes' - out = sfsi_toy_gaussian.run_inversion(mesh_path, nx, ny, noise_variance, prior_param) + prior_param = {"gamma": 0.1, "delta": 2.} + mesh_filename = '../../example/meshes/circle.xdmf' + out = sfsi_toy_gaussian.run_inversion(mesh_filename, nx, ny, noise_variance, prior_param) - #convergence of optimizer self.assertEqual(out['optimizer_results']['optimizer'],True,"Did not converge") From 1c390abdff998a094b7901207f9e497be2ca54d5 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Mon, 18 Mar 2024 14:34:33 -0500 Subject: [PATCH 40/50] MPI lag fixed --- example/sfsi_toy_gaussian.py | 1 + hippylibX/modeling/PDEProblem.py | 36 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/example/sfsi_toy_gaussian.py b/example/sfsi_toy_gaussian.py index c55f01ed..35beac5c 100644 --- a/example/sfsi_toy_gaussian.py +++ b/example/sfsi_toy_gaussian.py @@ -133,6 +133,7 @@ def run_inversion(mesh_filename: str, nx : int, ny : int, noise_variance : float x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + if rank == 0: print( sep, "Find the MAP point", sep) diff --git a/hippylibX/modeling/PDEProblem.py b/hippylibX/modeling/PDEProblem.py index 96518a18..de7937cb 100644 --- a/hippylibX/modeling/PDEProblem.py +++ b/hippylibX/modeling/PDEProblem.py @@ -44,31 +44,31 @@ def __init__(self, Vh : list, varf_handler, bc=[], bc0=[], is_fwd_linear=False): self.petsc_options = {"ksp_type": "preonly","pc_type": "lu","pc_factor_mat_solver_type":"mumps"} - # def __del__(self): - # self.solver.destroy() - # self.solver_fwd_inc.destroy() - # self.solver_adj_inc.destroy() + def __del__(self): + # self.solver.destroy() + # self.solver_fwd_inc.destroy() + # self.solver_adj_inc.destroy() - # if(self.Wuu is not None): - # self.Wuu.destroy() + if(self.Wuu is not None): + self.Wuu.destroy() - # if(self.Wmu is not None): - # self.Wmu.destroy() + if(self.Wmu is not None): + self.Wmu.destroy() - # if(self.Wum is not None): - # self.Wum.destroy() + if(self.Wum is not None): + self.Wum.destroy() - # if(self.Wmm is not None): - # self.Wmm.destroy() + if(self.Wmm is not None): + self.Wmm.destroy() - # if(self.A is not None): - # self.A.destroy() + if(self.A is not None): + self.A.destroy() - # if(self.At is not None): - # self.At.destroy() + if(self.At is not None): + self.At.destroy() - # if(self.C is not None): - # self.C.destroy() + if(self.C is not None): + self.C.destroy() def generate_state(self) -> dlx.la.Vector: From 551bbbb0f5dbf73013b8e87435b791dcdea80524 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Mon, 18 Mar 2024 18:52:45 -0500 Subject: [PATCH 41/50] removing commented code --- hippylibX/modeling/Regularization.py | 80 ++++++++++++++++++++-------- hippylibX/modeling/misfit.py | 5 +- hippylibX/modeling/model.py | 1 - 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/hippylibX/modeling/Regularization.py b/hippylibX/modeling/Regularization.py index 9a3c62a6..acf2e7c3 100644 --- a/hippylibX/modeling/Regularization.py +++ b/hippylibX/modeling/Regularization.py @@ -1,46 +1,80 @@ import dolfinx as dlx import ufl import petsc4py -from .variables import STATE, PARAMETER, ADJOINT -from ..utils.vector2function import vector2Function, updateFromVector from mpi4py import MPI import numpy as np +import hippylibX as hpx +import dolfinx as dlx + class VariationalRegularization: - def __init__(self, Vh, functional_handler, isQuadratic=False): - self.Vh = Vh #Function space of the parameter. + def __init__(self, Vh : list, functional_handler, isQuadratic=False): + self.Vh = Vh self.functional_handler = functional_handler #a function or a functor that takes as input m (as dl.Function) and evaluate the regularization functional self.isQuadratic = isQuadratic #Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian dependes on m - self.xfun = dlx.fem.Function(self.Vh) - - def cost(self,m): - updateFromVector(self.xfun, m) - mfun = self.xfun - loc_cost = self.functional_handler(mfun) + self.xfun = [dlx.fem.Function(Vhi) for Vhi in Vh] + self.x_test = [ufl.TestFunction(Vh[hpx.STATE]), ufl.TestFunction(Vh[hpx.PARAMETER])] + + + def cost(self,x : list): + + hpx.updateFromVector(self.xfun[hpx.STATE],x[hpx.STATE]) + u_fun = self.xfun[hpx.STATE] + + hpx.updateFromVector(self.xfun[hpx.PARAMETER], x[hpx.PARAMETER]) + m_fun = self.xfun[hpx.PARAMETER] + + loc_cost = self.functional_handler(u_fun, m_fun) glb_cost_proc = dlx.fem.assemble_scalar(dlx.fem.form(loc_cost)) - return self.Vh[STATE].mesh.comm.allreduce(glb_cost_proc, op=MPI.SUM ) - - - def grad(self, x : dlx.la.Vector, out: dlx.la.Vector) -> None: - updateFromVector(self.xfun, x) - mfun = self.xfun - L = dlx.fem.form(ufl.derivative(self.functional_handler(mfun),mfun,ufl.TestFunction(self.Vh))) + return self.Vh[hpx.STATE].mesh.comm.allreduce(glb_cost_proc, op=MPI.SUM ) + + + def grad(self, i : int, x : list, out: dlx.la.Vector) -> dlx.la.Vector: + + hpx.updateFromVector(self.xfun[hpx.STATE],x[hpx.STATE]) + u_fun = self.xfun[hpx.STATE] + + hpx.updateFromVector(self.xfun[hpx.PARAMETER],x[hpx.PARAMETER]) + m_fun = self.xfun[hpx.PARAMETER] + + x_fun = [u_fun, m_fun] + out.array[:] = 0. tmp_out = dlx.la.create_petsc_vector_wrap(out) - tmp_out.ghostUpdate(addv=petsc4py.PETSc.InsertMode.ADD_VALUES, mode=petsc4py.PETSc.ScatterMode.REVERSE) + dlx.fem.petsc.assemble_vector( tmp_out, dlx.fem.form(ufl.derivative( self.functional_handler(*x_fun), x_fun[i], self.x_test[i])) ) + tmp_out.ghostUpdate(petsc4py.PETSc.InsertMode.ADD_VALUES,petsc4py.PETSc.ScatterMode.REVERSE) tmp_out.destroy() - def setLinearizationPoint(self, m, rel_tol=1e-12, max_iter=1000): - updateFromVector(self.xfun, m) - mfun = self.xfun - L = ufl.derivative(ufl.derivative(self.functional_handler(mfun),mfun), mfun) + + def setLinearizationPoint(self, x: list, rel_tol=1e-12, max_iter=1000): + + hpx.updateFromVector(self.xfun[hpx.STATE], x[hpx.STATE]) + u_fun = self.xfun + + hpx.updateFromVector(self.xfun[hpx.PARAMETER], x[hpx.PARAMETER]) + m_fun = self.xfun + + self.x_lin_fn = [u_fun, m_fun] + + L = ufl.derivative(ufl.derivative(self.functional_handler(*self.x_lin_fn),m_fun,self.x_test[hpx.PARAMETER]), m_fun, self.x_test[hpx.PARAMETER]) self.R = dlx.fem.petsc.assemble_matrix(dlx.fem.form(L)) self.R.assemble() self.Rsolver = petsc4py.PETSc.KSP().create() self.Rsolver.getPC().setType(petsc4py.PETSc.PC.Type.GAMG) self.Rsolver.setType(petsc4py.PETSc.KSP.Type.CG) - self.Rsolver.setIterationNumber(max_iter) #these values should be supplied as arguments. + self.Rsolver.setIterationNumber(max_iter) self.Rsolver.setTolerances(rtol=rel_tol) self.Rsolver.setErrorIfNotConverged(True) self.Rsolver.setInitialGuessNonzero(False) self.Rsolver.setOperators(self.R) + + def apply_ij(self,i : int,j : int, dir : dlx.la.Vector, out : dlx.la.Vector): + + form = self.form(*self.x_lin_fun[hpx.PARAMETER]) + dir_fun = hpx.vector2Function(dir, self.Vh[j]) + action = dlx.fem.form(ufl.derivative( ufl.derivative(form, self.x_lin_fun[i], self.x_test[i]), self.x_lin_fun[j], dir_fun )) + out.array[:] = 0. + tmp_out = dlx.la.create_petsc_vector_wrap(out) + dlx.fem.petsc.assemble_vector(tmp_out, action) + tmp_out.ghostUpdate(petsc4py.PETSc.InsertMode.ADD_VALUES,petsc4py.PETSc.ScatterMode.REVERSE) + tmp_out.destroy() diff --git a/hippylibX/modeling/misfit.py b/hippylibX/modeling/misfit.py index c9e5b1da..5c1820cc 100644 --- a/hippylibX/modeling/misfit.py +++ b/hippylibX/modeling/misfit.py @@ -38,8 +38,6 @@ def grad(self, i : int, x : list, out: dlx.la.Vector) -> dlx.la.Vector: x_fun = [u_fun, m_fun] - L = dlx.fem.form(ufl.derivative( self.form(*x_fun), x_fun[i], self.x_test[i])) - out.array[:] = 0. tmp_out = dlx.la.create_petsc_vector_wrap(out) dlx.fem.petsc.assemble_vector( tmp_out, dlx.fem.form(ufl.derivative( self.form(*x_fun), x_fun[i], self.x_test[i])) ) @@ -71,4 +69,5 @@ def apply_ij(self,i : int,j : int, dir : dlx.la.Vector, out : dlx.la.Vector): tmp_out = dlx.la.create_petsc_vector_wrap(out) dlx.fem.petsc.assemble_vector(tmp_out, action) tmp_out.ghostUpdate(petsc4py.PETSc.InsertMode.ADD_VALUES,petsc4py.PETSc.ScatterMode.REVERSE) - tmp_out.destroy() \ No newline at end of file + tmp_out.destroy() + \ No newline at end of file diff --git a/hippylibX/modeling/model.py b/hippylibX/modeling/model.py index abe1bb07..2129c4ec 100644 --- a/hippylibX/modeling/model.py +++ b/hippylibX/modeling/model.py @@ -300,7 +300,6 @@ def applyWum(self, dm, out): self.misfit.apply_ij(STATE,PARAMETER, dm, tmp) out.array[:] += tmp.array - def applyWmu(self, du, out): """ Apply the :math:`W_{mu}` block of the Hessian to a (incremental) state variable. From 776e6e85f6a31e79916f005d559900b8b432f409 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Tue, 19 Mar 2024 09:12:13 -0500 Subject: [PATCH 42/50] prior del method, jacobi pc --- example/poisson_example.py | 8 -------- hippylibX/modeling/prior.py | 20 ++++++++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/example/poisson_example.py b/example/poisson_example.py index c30a391d..e94e515a 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -77,15 +77,8 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict pde.solveFwd(u_true,x_true) - xfun = [dlx.fem.Function(Vhi) for Vhi in Vh] # LIKELIHOOD - hpx.updateFromVector(xfun[hpx.STATE],u_true) - u_fun_true = xfun[hpx.STATE] - - hpx.updateFromVector(xfun[hpx.PARAMETER],m_true) - m_fun_true = xfun[hpx.PARAMETER] - d = dlx.fem.Function(Vh[hpx.STATE]) d.x.array[:] = u_true.array[:] hpx.parRandom.normal_perturb(np.sqrt(noise_variance),d.x) @@ -106,7 +99,6 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict hpx.parRandom.normal(1.,noise) prior.sample(noise,m0) - data_misfit_True = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) data_misfit_False = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) diff --git a/hippylibX/modeling/prior.py b/hippylibX/modeling/prior.py index 8f143ca4..7434f322 100644 --- a/hippylibX/modeling/prior.py +++ b/hippylibX/modeling/prior.py @@ -107,18 +107,11 @@ def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) self.ds = ufl.Measure("ds",metadata={"quadrature_degree":4}) - self.Vh = Vh self.sqrt_precision_varf_handler = sqrt_precision_varf_handler - - - # self.petsc_options = {"ksp_type": "cg","pc_type": "hypre"} - - # self.petsc_options = {"ksp_type": "preonly","pc_type": "lu","pc_factor_mat_solver_type":"mumps"} self.petsc_options = {"ksp_type": "cg","pc_type": "jacobi"} - trial = ufl.TrialFunction(Vh) test = ufl.TestFunction(Vh) @@ -133,10 +126,14 @@ def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean self.Msolver.setErrorIfNotConverged(True) self.Msolver.setInitialGuessNonzero(False) self.Msolver.setOperators(self.M) + + # print(self.Msolver.view()) self.A = dlx.fem.petsc.assemble_matrix(dlx.fem.form(sqrt_precision_varf_handler(trial, test) )) self.A.assemble() self.Asolver = self._createsolver() + # print(self.Asolver.view()) + if(self.petsc_options['pc_type'] == 'hypre'): pc = self.Asolver.getPC() pc.setHYPREType('boomeramg') @@ -261,7 +258,6 @@ def cost(self,m : dlx.la.Vector) -> float: return return_value - def grad(self,m : dlx.la.Vector, out : dlx.la.Vector) -> None: temp_petsc_vec_d = dlx.la.create_petsc_vector_wrap(m).copy() temp_petsc_vec_self_mean = dlx.la.create_petsc_vector_wrap(self.mean) @@ -276,6 +272,14 @@ def grad(self,m : dlx.la.Vector, out : dlx.la.Vector) -> None: temp_petsc_vec_out.destroy() + def __del__(self): + self.Msolver.destroy() + self.Asolver.destroy() + self.M.destroy() + self.A.destroy() + self.sqrtM.destroy() + + def BiLaplacianPrior(Vh : dlx.fem.FunctionSpace, gamma : float, delta : float, Theta = None, mean=None, rel_tol=1e-12, max_iter=1000, robin_bc=False) -> test_prior: """ This function construct an instance of :code"`SqrtPrecisionPDE_Prior` with covariance matrix From b76c9ce8d23c1a6198d5bca03af0247cd302cb22 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Tue, 19 Mar 2024 09:13:15 -0500 Subject: [PATCH 43/50] need to fix Regularization.py --- hippylibX/modeling/Regularization.py | 45 ++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/hippylibX/modeling/Regularization.py b/hippylibX/modeling/Regularization.py index acf2e7c3..1a933893 100644 --- a/hippylibX/modeling/Regularization.py +++ b/hippylibX/modeling/Regularization.py @@ -7,8 +7,25 @@ import dolfinx as dlx +# an example of functional_handler can be: +# class H1TikhonvFunctional: +# def __init__(self, gamma, delta, m0): +# self.gamma = gamma #These are dlx Constant, Expression, or Function +# self.delta = delta +# self.m0 = m0 + +# def __call__(self, m): #Here m is a dlx Function +# return ufl.inner(self.gamma * ufl.grad(m), ufl.grad(m) ) *ufl.dx + \ +# ufl.inner(self.delta * m, m)*ufl.dx + + class VariationalRegularization: def __init__(self, Vh : list, functional_handler, isQuadratic=False): + # self.Vh = Vh #Function space of the parameter. + # self.functional_handler = functional_handler #a function or a functor that takes as input m (as dl.Function) and evaluate the regularization functional + # self.isQuadratic = isQuadratic #Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian dependes on m + + self.Vh = Vh self.functional_handler = functional_handler #a function or a functor that takes as input m (as dl.Function) and evaluate the regularization functional self.isQuadratic = isQuadratic #Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian dependes on m @@ -18,6 +35,10 @@ def __init__(self, Vh : list, functional_handler, isQuadratic=False): def cost(self,x : list): + # 1. Cast the petsc4py vector m to a dlx.Function mfun + # 2. Compute the cost by calling assemble on self.functional_handler(mfun) + # 3. Return the value of the cost (Make sure to call a AllReduce for parallel computations + hpx.updateFromVector(self.xfun[hpx.STATE],x[hpx.STATE]) u_fun = self.xfun[hpx.STATE] @@ -31,6 +52,10 @@ def cost(self,x : list): def grad(self, i : int, x : list, out: dlx.la.Vector) -> dlx.la.Vector: + # 1. Cast the petsc4py vector m to a dlx.Function mfun + # 2. call symbolic differentation of self.functional_handler(mfun) wrt mfum + # 3. call assemble, update ghosts, and store the result in out + hpx.updateFromVector(self.xfun[hpx.STATE],x[hpx.STATE]) u_fun = self.xfun[hpx.STATE] @@ -47,7 +72,12 @@ def grad(self, i : int, x : list, out: dlx.la.Vector) -> dlx.la.Vector: def setLinearizationPoint(self, x: list, rel_tol=1e-12, max_iter=1000): - + + # 1. Cast the petsc4py vector m to a dlx.Function mfun + # 2. call symbolic differentiation (twice) to get the second variation of self.functional_handler(mfun) wrt mfun + # 3. assemble the Hessian operator (it's a sparse matrix!) in the attribute self.R + # 4. set up a linearsolver self.Rsolver that uses CG as Krylov method, gamg as preconditioner and self.R as operator + hpx.updateFromVector(self.xfun[hpx.STATE], x[hpx.STATE]) u_fun = self.xfun @@ -66,15 +96,4 @@ def setLinearizationPoint(self, x: list, rel_tol=1e-12, max_iter=1000): self.Rsolver.setTolerances(rtol=rel_tol) self.Rsolver.setErrorIfNotConverged(True) self.Rsolver.setInitialGuessNonzero(False) - self.Rsolver.setOperators(self.R) - - def apply_ij(self,i : int,j : int, dir : dlx.la.Vector, out : dlx.la.Vector): - - form = self.form(*self.x_lin_fun[hpx.PARAMETER]) - dir_fun = hpx.vector2Function(dir, self.Vh[j]) - action = dlx.fem.form(ufl.derivative( ufl.derivative(form, self.x_lin_fun[i], self.x_test[i]), self.x_lin_fun[j], dir_fun )) - out.array[:] = 0. - tmp_out = dlx.la.create_petsc_vector_wrap(out) - dlx.fem.petsc.assemble_vector(tmp_out, action) - tmp_out.ghostUpdate(petsc4py.PETSc.InsertMode.ADD_VALUES,petsc4py.PETSc.ScatterMode.REVERSE) - tmp_out.destroy() + self.Rsolver.setOperators(self.R) \ No newline at end of file From 7a505359760d33fa6512cc5f29b19bc4680314b2 Mon Sep 17 00:00:00 2001 From: Umberto Villa Date: Tue, 19 Mar 2024 10:08:43 -0500 Subject: [PATCH 44/50] Update Regularization.py --- hippylibX/modeling/Regularization.py | 73 +++++++++++++++------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/hippylibX/modeling/Regularization.py b/hippylibX/modeling/Regularization.py index 1a933893..c8b97a08 100644 --- a/hippylibX/modeling/Regularization.py +++ b/hippylibX/modeling/Regularization.py @@ -20,80 +20,87 @@ class VariationalRegularization: - def __init__(self, Vh : list, functional_handler, isQuadratic=False): + def __init__(self, Vh : dlx.FunctionSpace, functional_handler, isQuadratic=False): # self.Vh = Vh #Function space of the parameter. # self.functional_handler = functional_handler #a function or a functor that takes as input m (as dl.Function) and evaluate the regularization functional # self.isQuadratic = isQuadratic #Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian dependes on m self.Vh = Vh - self.functional_handler = functional_handler #a function or a functor that takes as input m (as dl.Function) and evaluate the regularization functional - self.isQuadratic = isQuadratic #Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian dependes on m - self.xfun = [dlx.fem.Function(Vhi) for Vhi in Vh] - self.x_test = [ufl.TestFunction(Vh[hpx.STATE]), ufl.TestFunction(Vh[hpx.PARAMETER])] + self.functional_handler = functional_handler #a function or a functor that takes as input m (as dlx.Function) and evaluates the regularization functional + self.isQuadratic = isQuadratic # Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian depends on m + self.mfun = dlx.fem.Function(Vh) + self.mtest = ufl.TestFunction(Vh) + self.mtrial = ufl.TrialFunction(Vh) + + self.petsc_option_M = {"ksp_type": "cg", "pc_type": "jacobi"} # see if you can set rtol atol maxiter + self.petsc_option_R = {"ksp_type": "cg", "pc_type": "hypre"} + + self.R = None + self.Rsolver= None + if(self.isQuadratic == True): + tmp = dlx.fem.Function(Vh).x + self.setLinearizationPoint(tmp) + + self.M = dlx.fem.petsc.assemble( dlx.form( ufl.inner(self.mtrial, self.mtest)*ufl.dx ) ) + self.M.assemble() + self.Msolver = ...... - def cost(self,x : list): + def __del__(self): + self.Rsolver.destroy() + self.R.destroy() + self.Msolver.destroy() + self.M.destroy() + + def cost(self,m : dlx.la.Vector): # 1. Cast the petsc4py vector m to a dlx.Function mfun # 2. Compute the cost by calling assemble on self.functional_handler(mfun) # 3. Return the value of the cost (Make sure to call a AllReduce for parallel computations - hpx.updateFromVector(self.xfun[hpx.STATE],x[hpx.STATE]) - u_fun = self.xfun[hpx.STATE] + hpx.updateFromVector(self.mfun,m) + cost_functional = self.functional_handler(mfun) + local_cost = dlx.fem.assemble_scalar(dlx.fem.form(cost_functional)) + return self.Vh.mesh.comm.allreduce(local_cost, op=MPI.SUM ) - hpx.updateFromVector(self.xfun[hpx.PARAMETER], x[hpx.PARAMETER]) - m_fun = self.xfun[hpx.PARAMETER] - - loc_cost = self.functional_handler(u_fun, m_fun) - glb_cost_proc = dlx.fem.assemble_scalar(dlx.fem.form(loc_cost)) - return self.Vh[hpx.STATE].mesh.comm.allreduce(glb_cost_proc, op=MPI.SUM ) - - def grad(self, i : int, x : list, out: dlx.la.Vector) -> dlx.la.Vector: + def grad(self, m: dlx.la.Vector, out: dlx.la.Vector) -> dlx.la.Vector: # 1. Cast the petsc4py vector m to a dlx.Function mfun # 2. call symbolic differentation of self.functional_handler(mfun) wrt mfum # 3. call assemble, update ghosts, and store the result in out - hpx.updateFromVector(self.xfun[hpx.STATE],x[hpx.STATE]) - u_fun = self.xfun[hpx.STATE] - - hpx.updateFromVector(self.xfun[hpx.PARAMETER],x[hpx.PARAMETER]) - m_fun = self.xfun[hpx.PARAMETER] + hpx.updateFromVector(self.mfun,m) - x_fun = [u_fun, m_fun] - out.array[:] = 0. tmp_out = dlx.la.create_petsc_vector_wrap(out) - dlx.fem.petsc.assemble_vector( tmp_out, dlx.fem.form(ufl.derivative( self.functional_handler(*x_fun), x_fun[i], self.x_test[i])) ) + dlx.fem.petsc.assemble_vector( tmp_out, dlx.fem.form(ufl.derivative( self.functional_handler(mfun), mfun[i], self.mtest)) ) tmp_out.ghostUpdate(petsc4py.PETSc.InsertMode.ADD_VALUES,petsc4py.PETSc.ScatterMode.REVERSE) tmp_out.destroy() - def setLinearizationPoint(self, x: list, rel_tol=1e-12, max_iter=1000): + def setLinearizationPoint(self, m: dlx.la.Vector, rel_tol=1e-12, max_iter=1000): #remove rel_tol, max_iter from this signature # 1. Cast the petsc4py vector m to a dlx.Function mfun # 2. call symbolic differentiation (twice) to get the second variation of self.functional_handler(mfun) wrt mfun # 3. assemble the Hessian operator (it's a sparse matrix!) in the attribute self.R # 4. set up a linearsolver self.Rsolver that uses CG as Krylov method, gamg as preconditioner and self.R as operator - hpx.updateFromVector(self.xfun[hpx.STATE], x[hpx.STATE]) - u_fun = self.xfun - - hpx.updateFromVector(self.xfun[hpx.PARAMETER], x[hpx.PARAMETER]) - m_fun = self.xfun + if (self.isQuadratic == True) and (self.R is not None): + return - self.x_lin_fn = [u_fun, m_fun] + hpx.updateFromVector(self.mfun, m) - L = ufl.derivative(ufl.derivative(self.functional_handler(*self.x_lin_fn),m_fun,self.x_test[hpx.PARAMETER]), m_fun, self.x_test[hpx.PARAMETER]) + L = ufl.derivative(ufl.derivative(self.functional_handler(self.mfun), self.mfun,self.mtest), self.mfun, self.mtrial) self.R = dlx.fem.petsc.assemble_matrix(dlx.fem.form(L)) self.R.assemble() self.Rsolver = petsc4py.PETSc.KSP().create() + #UPDATE USING A PETSC OPTION. self.Rsolver.getPC().setType(petsc4py.PETSc.PC.Type.GAMG) self.Rsolver.setType(petsc4py.PETSc.KSP.Type.CG) self.Rsolver.setIterationNumber(max_iter) self.Rsolver.setTolerances(rtol=rel_tol) self.Rsolver.setErrorIfNotConverged(True) self.Rsolver.setInitialGuessNonzero(False) - self.Rsolver.setOperators(self.R) \ No newline at end of file + self.Rsolver.setOperators(self.R) From 5ffc3f8932969464fdbc113a39e077d0fdfe79bc Mon Sep 17 00:00:00 2001 From: Umberto Villa Date: Tue, 19 Mar 2024 10:10:31 -0500 Subject: [PATCH 45/50] Update prior.py --- hippylibX/modeling/prior.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hippylibX/modeling/prior.py b/hippylibX/modeling/prior.py index 7434f322..7164922b 100644 --- a/hippylibX/modeling/prior.py +++ b/hippylibX/modeling/prior.py @@ -271,6 +271,9 @@ def grad(self,m : dlx.la.Vector, out : dlx.la.Vector) -> None: temp_petsc_vec_self_mean.destroy() temp_petsc_vec_out.destroy() + def setLinearizationPoint(self, m : dlx.la.Vector) -> None: + return + def __del__(self): self.Msolver.destroy() @@ -319,4 +322,4 @@ def sqrt_precision_varf_handler(trial : ufl.TrialFunction, test : ufl.TestFuncti return gamma*varfL + delta*varfM + robin_coeff*varf_robin - return test_prior(Vh, sqrt_precision_varf_handler, mean, rel_tol, max_iter) \ No newline at end of file + return test_prior(Vh, sqrt_precision_varf_handler, mean, rel_tol, max_iter) From f83d5d445eccbf0925a4980c33b61bb69d65912c Mon Sep 17 00:00:00 2001 From: Umberto Villa Date: Tue, 19 Mar 2024 10:11:28 -0500 Subject: [PATCH 46/50] Update model.py --- hippylibX/modeling/model.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hippylibX/modeling/model.py b/hippylibX/modeling/model.py index 2129c4ec..ea6a762b 100644 --- a/hippylibX/modeling/model.py +++ b/hippylibX/modeling/model.py @@ -202,8 +202,7 @@ def setPointForHessianEvaluations(self, x : list, gauss_newton_approx=False): self.gauss_newton_approx = gauss_newton_approx self.problem.setLinearizationPoint(x, self.gauss_newton_approx) self.misfit.setLinearizationPoint(x, self.gauss_newton_approx) - if hasattr(self.prior, "setLinearizationPoint"): - self.prior.setLinearizationPoint(x[PARAMETER], self.gauss_newton_approx) + self.prior.setLinearizationPoint(x[PARAMETER], self.gauss_newton_approx) def solveFwdIncremental(self, sol : dlx.la.Vector, rhs : dlx.la.Vector): @@ -391,4 +390,4 @@ def apply_ij(self, i, j, d, out): elif i == ADJOINT and j == PARAMETER: self.applyC(d,out) else: - raise IndexError("apply_ij not allowed for i = {0}, j = {1}".format(i,j)) \ No newline at end of file + raise IndexError("apply_ij not allowed for i = {0}, j = {1}".format(i,j)) From dc78353965a757f9556f75da9fb19c71197367fb Mon Sep 17 00:00:00 2001 From: Umberto Villa Date: Tue, 19 Mar 2024 10:12:13 -0500 Subject: [PATCH 47/50] Update prior.py --- hippylibX/modeling/prior.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hippylibX/modeling/prior.py b/hippylibX/modeling/prior.py index 7164922b..6d300832 100644 --- a/hippylibX/modeling/prior.py +++ b/hippylibX/modeling/prior.py @@ -271,7 +271,7 @@ def grad(self,m : dlx.la.Vector, out : dlx.la.Vector) -> None: temp_petsc_vec_self_mean.destroy() temp_petsc_vec_out.destroy() - def setLinearizationPoint(self, m : dlx.la.Vector) -> None: + def setLinearizationPoint(self, m : dlx.la.Vector, gauss_newton_approx = False) -> None: return From 9a0e8c2ff9af02dd6575addf6a0884602fa24375 Mon Sep 17 00:00:00 2001 From: Umberto Villa Date: Tue, 19 Mar 2024 10:13:24 -0500 Subject: [PATCH 48/50] Update Regularization.py --- hippylibX/modeling/Regularization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hippylibX/modeling/Regularization.py b/hippylibX/modeling/Regularization.py index c8b97a08..bd05df4e 100644 --- a/hippylibX/modeling/Regularization.py +++ b/hippylibX/modeling/Regularization.py @@ -80,7 +80,7 @@ def grad(self, m: dlx.la.Vector, out: dlx.la.Vector) -> dlx.la.Vector: tmp_out.destroy() - def setLinearizationPoint(self, m: dlx.la.Vector, rel_tol=1e-12, max_iter=1000): #remove rel_tol, max_iter from this signature + def setLinearizationPoint(self, m: dlx.la.Vector, gauss_newton_approx = False, rel_tol=1e-12, max_iter=1000): #remove rel_tol, max_iter from this signature # 1. Cast the petsc4py vector m to a dlx.Function mfun # 2. call symbolic differentiation (twice) to get the second variation of self.functional_handler(mfun) wrt mfun From c56ec6399fa94b84d51b15f1607c8123bf2fc994 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Tue, 19 Mar 2024 15:56:29 -0500 Subject: [PATCH 49/50] regularization prior example added --- example/poisson_example_reg.py | 185 ++++++++++++++++++++ example/sfsi_toy_gaussian_reg.py | 209 +++++++++++++++++++++++ hippylibX/algorithms/NewtonCG.py | 6 +- hippylibX/algorithms/cgsolverSteihaug.py | 21 ++- hippylibX/modeling/Regularization.py | 89 ++++------ hippylibX/modeling/__init__.py | 4 - hippylibX/modeling/model.py | 4 +- hippylibX/modeling/prior.py | 41 ++--- 8 files changed, 467 insertions(+), 92 deletions(-) create mode 100644 example/poisson_example_reg.py create mode 100644 example/sfsi_toy_gaussian_reg.py diff --git a/example/poisson_example_reg.py b/example/poisson_example_reg.py new file mode 100644 index 00000000..7386abc4 --- /dev/null +++ b/example/poisson_example_reg.py @@ -0,0 +1,185 @@ +# poisson example using VariationalRegularization prior instead +# of BiLaplacian Prior + +import ufl +import dolfinx as dlx +from mpi4py import MPI +import numpy as np +import petsc4py + +import sys +import os +import dolfinx.fem.petsc + +from matplotlib import pyplot as plt + +sys.path.append( os.environ.get('HIPPYLIBX_BASE_DIR', "../") ) +import hippylibX as hpx + +def master_print(comm, *args, **kwargs): + if comm.rank == 0: + print(*args, **kwargs) + +class Poisson_Approximation: + def __init__(self, alpha : float, f : float): + + self.alpha = alpha + self.f = f + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + self.ds = ufl.Measure("ds",metadata={"quadrature_degree":4}) + + def __call__(self, u: dlx.fem.Function, m : dlx.fem.Function, p : dlx.fem.Function) -> ufl.form.Form: + return ufl.exp(m) * ufl.inner(ufl.grad(u), ufl.grad(p))*self.dx + \ + self.alpha * ufl.inner(u,p)*self.ds - self.f*p*self.dx + + +class PoissonMisfitForm: + def __init__(self, d : float, sigma2 : float): + self.d = d + self.sigma2 = sigma2 + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + + def __call__(self, u : dlx.fem.Function, m: dlx.fem.Function) -> ufl.form.Form: + return .5/self.sigma2*ufl.inner(u - self.d, u - self.d)*self.dx + + +# functional handler for prior: +class H1TikhonvFunctional: + def __init__(self, gamma, delta): + self.gamma = gamma #These are dlx Constant, Expression, or Function + self.delta = delta + # self.m0 = m0 + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + + + def __call__(self, m): #Here m is a dlx Function + return ufl.inner(self.gamma * ufl.grad(m), ufl.grad(m) ) *self.dx + \ + ufl.inner(self.delta * m, m)*self.dx + +def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: + sep = "\n"+"#"*80+"\n" + + comm = MPI.COMM_WORLD + rank = comm.rank + nproc = comm.size + + msh = dlx.mesh.create_unit_square(comm, nx, ny) + + Vh_phi = dlx.fem.FunctionSpace(msh, ("CG", 2)) + Vh_m = dlx.fem.FunctionSpace(msh, ("CG", 1)) + Vh = [Vh_phi, Vh_m, Vh_phi] + + ndofs = [Vh_phi.dofmap.index_map.size_global * Vh_phi.dofmap.index_map_bs, Vh_m.dofmap.index_map.size_global * Vh_m.dofmap.index_map_bs ] + master_print (comm, sep, "Set up the mesh and finite element spaces", sep) + master_print (comm, "Number of dofs: STATE={0}, PARAMETER={1}".format(*ndofs) ) + + # FORWARD MODEL + alpha = 100. + f = 1. + pde_handler = Poisson_Approximation(alpha, f) + pde = hpx.PDEVariationalProblem(Vh, pde_handler, [], [], is_fwd_linear=True) + + # GROUND TRUTH + m_true = dlx.fem.Function(Vh_m) + + m_true.interpolate(lambda x: np.log(2 + 7*( ( (x[0] - 0.5)**2 + (x[1] - 0.5)**2)**0.5 > 0.2)) ) + m_true.x.scatter_forward() + + m_true = m_true.x + + u_true = pde.generate_state() + + x_true = [u_true, m_true, None] + + pde.solveFwd(u_true,x_true) + + + # LIKELIHOOD + d = dlx.fem.Function(Vh[hpx.STATE]) + d.x.array[:] = u_true.array[:] + hpx.parRandom.normal_perturb(np.sqrt(noise_variance),d.x) + d.x.scatter_forward() + + misfit_form = PoissonMisfitForm(d,noise_variance) + misfit = hpx.NonGaussianContinuousMisfit(Vh, misfit_form) + + prior_mean = dlx.fem.Function(Vh_m) + prior_mean.x.array[:] = 0.01 + prior_mean = prior_mean.x + + prior_gamma = prior_param['gamma'] + prior_delta = prior_param['delta'] + + prior_handler = H1TikhonvFunctional(prior_gamma, prior_delta) + prior = hpx.VariationalRegularization(Vh_m, prior_handler) + model = hpx.Model(pde, prior, misfit) + + m0 = pde.generate_parameter() + hpx.parRandom.normal(1.,m0) + + data_misfit_True = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + + data_misfit_False = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + + # # # ####################################### + + prior_mean_copy = pde.generate_parameter() + prior_mean_copy.array[:] = prior_mean.array[:] + + x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + + if rank == 0: + print( sep, "Find the MAP point", sep) + + parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + parameters["rel_tolerance"] = 1e-6 + parameters["abs_tolerance"] = 1e-9 + parameters["max_iter"] = 500 + parameters["cg_coarse_tolerance"] = 5e-1 + parameters["globalization"] = "LS" + parameters["GN_iter"] = 20 + if rank != 0: + parameters["print_level"] = -1 + + solver = hpx.ReducedSpaceNewtonCG(model, parameters) + + x = solver.solve(x) + + if solver.converged: + master_print(comm, "\nConverged in ", solver.it, " iterations.") + else: + master_print(comm, "\nNot Converged") + + master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + master_print (comm, "Final cost: ", solver.final_cost) + + optimizer_results = {} + if(solver.termination_reasons[solver.reason] == 'Norm of the gradient less than tolerance'): + optimizer_results['optimizer'] = True + else: + optimizer_results['optimizer'] = False + + + final_results = {"data_misfit_True":data_misfit_True, + "data_misfit_False":data_misfit_False, + "optimizer_results":optimizer_results} + + + return final_results + + + ####################################### + +if __name__ == "__main__": + nx = 64 + ny = 64 + noise_variance = 1e-4 + prior_param = {"gamma": 0.1, "delta": 1.} + run_inversion(nx, ny, noise_variance, prior_param) + + comm = MPI.COMM_WORLD + if(comm.rank == 0): + plt.savefig("poisson_result_FD_Gradient_Hessian_Check") + plt.show() + diff --git a/example/sfsi_toy_gaussian_reg.py b/example/sfsi_toy_gaussian_reg.py new file mode 100644 index 00000000..6cb12a9a --- /dev/null +++ b/example/sfsi_toy_gaussian_reg.py @@ -0,0 +1,209 @@ +import ufl +import dolfinx as dlx +from mpi4py import MPI +import numpy as np +import petsc4py + +import sys +import os +import dolfinx.fem.petsc + +from matplotlib import pyplot as plt + +sys.path.append( os.environ.get('HIPPYLIBX_BASE_DIR', "../") ) + +import hippylibX as hpx + +def master_print(comm, *args, **kwargs): + if comm.rank == 0: + print(*args, **kwargs) + +class DiffusionApproximation: + def __init__(self, D : float, u0 : float): + """ + Define the forward model for the diffusion approximation to radiative transfer equations + + D: diffusion coefficient 1/mu_eff with mu_eff = sqrt(3 mu_a (mu_a + mu_ps) ), where mu_a + is the unknown absorption coefficient, and mu_ps is the reduced scattering coefficient + + u0: Incident fluence (Robin condition) + + ds: boundary integrator for Robin condition + """ + self.D = D + self.u0 = u0 + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + self.ds = ufl.Measure("ds",metadata={"quadrature_degree":4}) + + + + def __call__(self, u: dlx.fem.Function, m : dlx.fem.Function, p : dlx.fem.Function) -> ufl.form.Form: + + + return ufl.inner(self.D*ufl.grad(u), ufl.grad(p))*ufl.dx(metadata={"quadrature_degree":4}) + \ + ufl.exp(m)*ufl.inner(u,p)*self.dx + \ + .5*ufl.inner(u-self.u0,p)*self.ds + + +class PACTMisfitForm: + def __init__(self, d : float, sigma2 : float): + self.sigma2 = sigma2 + self.d = d + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + + def __call__(self,u : dlx.fem.Function, m : dlx.fem.Function) -> ufl.form.Form: + return .5/self.sigma2*ufl.inner(u*ufl.exp(m) -self.d, u*ufl.exp(m) -self.d)*self.dx + + +# functional handler for prior: +class H1TikhonvFunctional: + def __init__(self, gamma, delta): + self.gamma = gamma #These are dlx Constant, Expression, or Function + self.delta = delta + # self.m0 = m0 + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + + + def __call__(self, m): #Here m is a dlx Function + return ufl.inner(self.gamma * ufl.grad(m), ufl.grad(m) ) *self.dx + \ + ufl.inner(self.delta * m, m)*self.dx + + +def run_inversion(mesh_filename: str, nx : int, ny : int, noise_variance : float, prior_param : dict) -> None: + sep = "\n"+"#"*80+"\n" + + comm = MPI.COMM_WORLD + rank = comm.rank + fname = mesh_filename + + fid = dlx.io.XDMFFile(comm,fname,"r") + msh = fid.read_mesh(name='mesh') + + Vh_phi = dlx.fem.FunctionSpace(msh, ("CG", 2)) + Vh_m = dlx.fem.FunctionSpace(msh, ("CG", 1)) + Vh = [Vh_phi, Vh_m, Vh_phi] + + ndofs = [Vh_phi.dofmap.index_map.size_global * Vh_phi.dofmap.index_map_bs, Vh_m.dofmap.index_map.size_global * Vh_m.dofmap.index_map_bs ] + master_print (comm, sep, "Set up the mesh and finite element spaces", sep) + master_print (comm, "Number of dofs: STATE={0}, PARAMETER={1}".format(*ndofs) ) + + # FORWARD MODEL + u0 = 1. + D = 1./24. + pde_handler = DiffusionApproximation(D, u0) + + pde = hpx.PDEVariationalProblem(Vh, pde_handler, [], [], is_fwd_linear=True) + + # GROUND TRUTH + m_true = dlx.fem.Function(Vh_m) + m_true.interpolate(lambda x: np.log(0.01) + 3.*( ( ( (x[0]-2.)*(x[0]-2.) + (x[1]-2.)*(x[1]-2.) ) < 1.) )) # + m_true.x.scatter_forward() + m_true = m_true.x + + u_true = pde.generate_state() + + x_true = [u_true, m_true, None] + + pde.solveFwd(u_true,x_true) + + xfun = [dlx.fem.Function(Vhi) for Vhi in Vh] + + # LIKELIHOOD + hpx.updateFromVector(xfun[hpx.STATE],u_true) + u_fun_true = xfun[hpx.STATE] + + hpx.updateFromVector(xfun[hpx.PARAMETER],m_true) + m_fun_true = xfun[hpx.PARAMETER] + + d = dlx.fem.Function(Vh[hpx.STATE]) + expr = u_fun_true * ufl.exp(m_fun_true) + hpx.projection(expr,d) + hpx.parRandom.normal_perturb(np.sqrt(noise_variance),d.x) + + d.x.scatter_forward() + + misfit_form = PACTMisfitForm(d, noise_variance) + misfit = hpx.NonGaussianContinuousMisfit(Vh, misfit_form) + + prior_mean = dlx.fem.Function(Vh_m) + prior_mean.x.array[:] = 0.01 + prior_mean = prior_mean.x + + prior_gamma = prior_param['gamma'] + prior_delta = prior_param['delta'] + + prior_handler = H1TikhonvFunctional(prior_gamma, prior_delta) + prior = hpx.VariationalRegularization(Vh_m, prior_handler) + + model = hpx.Model(pde, prior, misfit) + + m0 = pde.generate_parameter() + hpx.parRandom.normal(1.,m0) + + data_misfit_True = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=True,verbose=(rank == 0)) + + data_misfit_False = hpx.modelVerify(model,m0,is_quadratic=False,misfit_only=False,verbose=(rank == 0)) + + # ####################################### + + prior_mean_copy = pde.generate_parameter() + prior_mean_copy.array[:] = prior_mean.array[:] + + x = [model.generate_vector(hpx.STATE), prior_mean_copy, model.generate_vector(hpx.ADJOINT)] + + if rank == 0: + print( sep, "Find the MAP point", sep) + + parameters = hpx.ReducedSpaceNewtonCG_ParameterList() + parameters["rel_tolerance"] = 1e-6 + parameters["abs_tolerance"] = 1e-9 + parameters["max_iter"] = 500 + parameters["cg_coarse_tolerance"] = 5e-1 + parameters["globalization"] = "LS" + parameters["GN_iter"] = 20 + if rank != 0: + parameters["print_level"] = -1 + + solver = hpx.ReducedSpaceNewtonCG(model, parameters) + + x = solver.solve(x) + + if solver.converged: + master_print(comm, "\nConverged in ", solver.it, " iterations.") + else: + master_print(comm, "\nNot Converged") + + master_print (comm, "Termination reason: ", solver.termination_reasons[solver.reason]) + master_print (comm, "Final gradient norm: ", solver.final_grad_norm) + master_print (comm, "Final cost: ", solver.final_cost) + + + optimizer_results = {} + if(solver.termination_reasons[solver.reason] == 'Norm of the gradient less than tolerance'): + optimizer_results['optimizer'] = True + else: + optimizer_results['optimizer'] = False + + final_results = {"data_misfit_True":data_misfit_True, + "data_misfit_False":data_misfit_False, + "optimizer_results":optimizer_results} + + return final_results + + + ####################################### + +if __name__ == "__main__": + nx = 64 + ny = 64 + noise_variance = 1e-6 + prior_param = {"gamma": 0.1, "delta": 2.} + mesh_filename = './meshes/circle.xdmf' + run_inversion(mesh_filename, nx, ny, noise_variance, prior_param) + + comm = MPI.COMM_WORLD + if(comm.rank == 0): + plt.savefig("qpact_result_FD_Gradient_Hessian_Check") + plt.show() + + diff --git a/hippylibX/algorithms/NewtonCG.py b/hippylibX/algorithms/NewtonCG.py index d05bd3c6..65601594 100644 --- a/hippylibX/algorithms/NewtonCG.py +++ b/hippylibX/algorithms/NewtonCG.py @@ -212,7 +212,8 @@ def _solve_ls(self,x): tolcg = min(cg_coarse_tolerance, math.sqrt(gradnorm/gradnorm_ini)) HessApply = ReducedHessian(self.model) - solver = CGSolverSteihaug(comm = self.model.prior.R.mpi_comm()) + # solver = CGSolverSteihaug(comm = self.model.prior.R.mpi_comm()) + solver = CGSolverSteihaug(comm = self.model.prior.Vh.mesh.comm) solver.set_operator(HessApply) solver.set_preconditioner(self.model.Rsolver()) solver.parameters["rel_tolerance"] = tolcg @@ -338,7 +339,8 @@ def _solve_tr(self,x): tolcg = min(cg_coarse_tolerance, math.sqrt(gradnorm/gradnorm_ini)) HessApply = ReducedHessian(self.model) - solver = CGSolverSteihaug(comm = self.model.prior.R.mpi_comm()) + # solver = CGSolverSteihaug(comm = self.model.prior.R.mpi_comm()) + solver = CGSolverSteihaug(comm = self.model.prior.Vh.mesh.comm) solver.set_operator(HessApply) solver.set_preconditioner(self.model.Rsolver()) if self.it > 1: diff --git a/hippylibX/algorithms/cgsolverSteihaug.py b/hippylibX/algorithms/cgsolverSteihaug.py index 297344b0..ddd0ed3d 100644 --- a/hippylibX/algorithms/cgsolverSteihaug.py +++ b/hippylibX/algorithms/cgsolverSteihaug.py @@ -19,6 +19,7 @@ import mpi4py from ..utils import vector2Function from .linalg import inner +import petsc4py def CGSolverSteihaug_ParameterList(): """ @@ -176,7 +177,14 @@ def solve(self,x,b): self.z.array[:] = 0. - self.B_solver.solve(self.z,self.r) #z = B^-1 r + if(isinstance(self.B_solver,petsc4py.PETSc.KSP)): + temp_self_z = dlx.la.create_petsc_vector_wrap(self.z) + temp_self_r = dlx.la.create_petsc_vector_wrap(self.r) + self.B_solver(temp_self_r, temp_self_z) #expects (b,x) + temp_self_z.destroy() + temp_self_r.destroy() + else: + self.B_solver.solve(self.z,self.r) #z = B^-1 r #giving (x,b) self.d.array[:] = self.z.array @@ -235,7 +243,16 @@ def solve(self,x,b): break self.r.array[:] -= alpha*self.Ad.array - self.B_solver.solve(self.z, self.r) # z = B^-1 r + + if(isinstance(self.B_solver,petsc4py.PETSc.KSP)): + temp_self_z = dlx.la.create_petsc_vector_wrap(self.z) + temp_self_r = dlx.la.create_petsc_vector_wrap(self.r) + self.B_solver(temp_self_r, temp_self_z) #expects (b,x) + temp_self_z.destroy() + temp_self_r.destroy() + else: + self.B_solver.solve(self.z,self.r) #z = B^-1 r #giving (x,b) + betanom = inner(self.r, self.z) if self.parameters["print_level"] == 1: print( " Iteration : ", self.iter, " (B r, r) = ", betanom) diff --git a/hippylibX/modeling/Regularization.py b/hippylibX/modeling/Regularization.py index bd05df4e..570828a6 100644 --- a/hippylibX/modeling/Regularization.py +++ b/hippylibX/modeling/Regularization.py @@ -7,100 +7,81 @@ import dolfinx as dlx -# an example of functional_handler can be: -# class H1TikhonvFunctional: -# def __init__(self, gamma, delta, m0): -# self.gamma = gamma #These are dlx Constant, Expression, or Function -# self.delta = delta -# self.m0 = m0 - -# def __call__(self, m): #Here m is a dlx Function -# return ufl.inner(self.gamma * ufl.grad(m), ufl.grad(m) ) *ufl.dx + \ -# ufl.inner(self.delta * m, m)*ufl.dx - class VariationalRegularization: - def __init__(self, Vh : dlx.FunctionSpace, functional_handler, isQuadratic=False): - # self.Vh = Vh #Function space of the parameter. - # self.functional_handler = functional_handler #a function or a functor that takes as input m (as dl.Function) and evaluate the regularization functional - # self.isQuadratic = isQuadratic #Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian dependes on m - - + def __init__(self, Vh : dlx.fem.FunctionSpace, functional_handler, isQuadratic=False): self.Vh = Vh self.functional_handler = functional_handler #a function or a functor that takes as input m (as dlx.Function) and evaluates the regularization functional self.isQuadratic = isQuadratic # Whether the functional is a quadratic form (i.e. the Hessian is constant) or not (the Hessian depends on m self.mfun = dlx.fem.Function(Vh) self.mtest = ufl.TestFunction(Vh) self.mtrial = ufl.TrialFunction(Vh) - - self.petsc_option_M = {"ksp_type": "cg", "pc_type": "jacobi"} # see if you can set rtol atol maxiter - self.petsc_option_R = {"ksp_type": "cg", "pc_type": "hypre"} + self.dx = ufl.Measure("dx",metadata={"quadrature_degree":4}) + + self.petsc_options_M = {"ksp_type": "cg", "pc_type": "jacobi", "ksp_rtol":"1e-12", "ksp_max_it":"1000", "ksp_error_if_not_converged":"true","ksp_initial_guess_nonzero":"false"} + self.petsc_options_R = {"ksp_type": "cg", "pc_type": "gamg", "ksp_rtol":"1e-12", "ksp_max_it":"1000", "ksp_error_if_not_converged":"true", "ksp_initial_guess_nonzero":"false"} self.R = None - self.Rsolver= None + self.Rsolver = None if(self.isQuadratic == True): tmp = dlx.fem.Function(Vh).x self.setLinearizationPoint(tmp) - self.M = dlx.fem.petsc.assemble( dlx.form( ufl.inner(self.mtrial, self.mtest)*ufl.dx ) ) + self.M = dlx.fem.petsc.assemble_matrix( dlx.fem.form( ufl.inner(self.mtrial, self.mtest)*self.dx ) ) self.M.assemble() - self.Msolver = ...... + + self.Msolver = self._createsolver(self.petsc_options_M) + self.Msolver.setOperators(self.M) def __del__(self): - self.Rsolver.destroy() - self.R.destroy() + if(self.Rsolver is not None): + self.Rsolver.destroy() + if(self.R is not None): + self.R.destroy() self.Msolver.destroy() self.M.destroy() - def cost(self,m : dlx.la.Vector): + def _createsolver(self,petsc_options) -> petsc4py.PETSc.KSP: + ksp = petsc4py.PETSc.KSP().create(self.Vh.mesh.comm) + problem_prefix = f"dolfinx_solve_{id(self)}" + ksp.setOptionsPrefix(problem_prefix) + + # Set PETSc options + opts = petsc4py.PETSc.Options() + opts.prefixPush(problem_prefix) - # 1. Cast the petsc4py vector m to a dlx.Function mfun - # 2. Compute the cost by calling assemble on self.functional_handler(mfun) - # 3. Return the value of the cost (Make sure to call a AllReduce for parallel computations + if petsc_options is not None: + for k, v in petsc_options.items(): + opts[k] = v + opts.prefixPop() + ksp.setFromOptions() + + return ksp + + def cost(self,m : dlx.la.Vector): hpx.updateFromVector(self.mfun,m) - cost_functional = self.functional_handler(mfun) + cost_functional = self.functional_handler(self.mfun) local_cost = dlx.fem.assemble_scalar(dlx.fem.form(cost_functional)) return self.Vh.mesh.comm.allreduce(local_cost, op=MPI.SUM ) def grad(self, m: dlx.la.Vector, out: dlx.la.Vector) -> dlx.la.Vector: - - # 1. Cast the petsc4py vector m to a dlx.Function mfun - # 2. call symbolic differentation of self.functional_handler(mfun) wrt mfum - # 3. call assemble, update ghosts, and store the result in out - hpx.updateFromVector(self.mfun,m) - out.array[:] = 0. tmp_out = dlx.la.create_petsc_vector_wrap(out) - dlx.fem.petsc.assemble_vector( tmp_out, dlx.fem.form(ufl.derivative( self.functional_handler(mfun), mfun[i], self.mtest)) ) + dlx.fem.petsc.assemble_vector( tmp_out, dlx.fem.form(ufl.derivative( self.functional_handler(self.mfun), self.mfun, self.mtest)) ) tmp_out.ghostUpdate(petsc4py.PETSc.InsertMode.ADD_VALUES,petsc4py.PETSc.ScatterMode.REVERSE) tmp_out.destroy() - def setLinearizationPoint(self, m: dlx.la.Vector, gauss_newton_approx = False, rel_tol=1e-12, max_iter=1000): #remove rel_tol, max_iter from this signature - - # 1. Cast the petsc4py vector m to a dlx.Function mfun - # 2. call symbolic differentiation (twice) to get the second variation of self.functional_handler(mfun) wrt mfun - # 3. assemble the Hessian operator (it's a sparse matrix!) in the attribute self.R - # 4. set up a linearsolver self.Rsolver that uses CG as Krylov method, gamg as preconditioner and self.R as operator - + def setLinearizationPoint(self, m: dlx.la.Vector, gauss_newton_approx = False): if (self.isQuadratic == True) and (self.R is not None): return - hpx.updateFromVector(self.mfun, m) - L = ufl.derivative(ufl.derivative(self.functional_handler(self.mfun), self.mfun,self.mtest), self.mfun, self.mtrial) self.R = dlx.fem.petsc.assemble_matrix(dlx.fem.form(L)) self.R.assemble() - self.Rsolver = petsc4py.PETSc.KSP().create() - #UPDATE USING A PETSC OPTION. - self.Rsolver.getPC().setType(petsc4py.PETSc.PC.Type.GAMG) - self.Rsolver.setType(petsc4py.PETSc.KSP.Type.CG) - self.Rsolver.setIterationNumber(max_iter) - self.Rsolver.setTolerances(rtol=rel_tol) - self.Rsolver.setErrorIfNotConverged(True) - self.Rsolver.setInitialGuessNonzero(False) + self.Rsolver = self._createsolver(self.petsc_options_R) self.Rsolver.setOperators(self.R) diff --git a/hippylibX/modeling/__init__.py b/hippylibX/modeling/__init__.py index dabd7baf..843dc326 100644 --- a/hippylibX/modeling/__init__.py +++ b/hippylibX/modeling/__init__.py @@ -1,12 +1,8 @@ -# from misfit import -# from model import -# from modelVerify import from .PDEProblem import PDEVariationalProblem from .variables import * from .Regularization import * from .misfit import * from .model import * from .modelVerify import * -from .Regularization import * from .prior import * from .reducedHessian import * \ No newline at end of file diff --git a/hippylibX/modeling/model.py b/hippylibX/modeling/model.py index ea6a762b..59f842d5 100644 --- a/hippylibX/modeling/model.py +++ b/hippylibX/modeling/model.py @@ -88,7 +88,9 @@ def init_parameter(self, m) -> dlx.la.Vector: """ Reshape :code:`m` so that it is compatible with the parameter variable """ - return self.prior.generate_parameter(0) + # return self.prior.generate_parameter(0) + return self.problem.generate_parameter() + def cost(self, x : list) -> list: """ diff --git a/hippylibX/modeling/prior.py b/hippylibX/modeling/prior.py index 6d300832..ad8c0062 100644 --- a/hippylibX/modeling/prior.py +++ b/hippylibX/modeling/prior.py @@ -88,8 +88,8 @@ def solve(self,x : dlx.la.Vector, b : dlx.la.Vector): return nit -class test_prior: - def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean=None, rel_tol=1e-12, max_iter=1000): +class SqrtPrecisionPDE_Prior: + def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean=None): """ Construct the prior model. @@ -110,7 +110,8 @@ def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean self.Vh = Vh self.sqrt_precision_varf_handler = sqrt_precision_varf_handler - self.petsc_options = {"ksp_type": "cg","pc_type": "jacobi"} + self.petsc_options_M = {"ksp_type": "cg", "pc_type": "jacobi", "ksp_rtol":"1e-12", "ksp_max_it":"1000", "ksp_error_if_not_converged":"true","ksp_initial_guess_nonzero":"false"} + self.petsc_options_A = {"ksp_type": "cg", "pc_type": "hypre", "ksp_rtol":"1e-12", "ksp_max_it":"1000", "ksp_error_if_not_converged":"true", "ksp_initial_guess_nonzero":"false"} trial = ufl.TrialFunction(Vh) test = ufl.TestFunction(Vh) @@ -120,28 +121,17 @@ def __init__(self, Vh : dlx.fem.FunctionSpace, sqrt_precision_varf_handler, mean self.M = dlx.fem.petsc.assemble_matrix(dlx.fem.form(varfM)) self.M.assemble() - self.Msolver = self._createsolver() - self.Msolver.setIterationNumber(max_iter) - self.Msolver.setTolerances(rtol=rel_tol) - self.Msolver.setErrorIfNotConverged(True) - self.Msolver.setInitialGuessNonzero(False) + self.Msolver = self._createsolver(self.petsc_options_M) self.Msolver.setOperators(self.M) - - # print(self.Msolver.view()) self.A = dlx.fem.petsc.assemble_matrix(dlx.fem.form(sqrt_precision_varf_handler(trial, test) )) self.A.assemble() - self.Asolver = self._createsolver() - # print(self.Asolver.view()) + self.Asolver = self._createsolver(self.petsc_options_A) - if(self.petsc_options['pc_type'] == 'hypre'): + if(self.petsc_options_A['pc_type'] == 'hypre'): pc = self.Asolver.getPC() pc.setHYPREType('boomeramg') - self.Asolver.setIterationNumber(max_iter) - self.Asolver.setTolerances(rtol=rel_tol) - self.Asolver.setErrorIfNotConverged(True) - self.Asolver.setInitialGuessNonzero(False) self.Asolver.setOperators(self.A) qdegree = 2*Vh._ufl_element.degree() @@ -220,7 +210,7 @@ def sample(self, noise : dlx.la.Vector, s : dlx.la.Vector, add_mean=True) -> Non temp_petsc_vec_s.destroy() - def _createsolver(self) -> petsc4py.PETSc.KSP: + def _createsolver(self,petsc_options) -> petsc4py.PETSc.KSP: ksp = petsc4py.PETSc.KSP().create(self.Vh.mesh.comm) problem_prefix = f"dolfinx_solve_{id(self)}" ksp.setOptionsPrefix(problem_prefix) @@ -231,17 +221,15 @@ def _createsolver(self) -> petsc4py.PETSc.KSP: #petsc options for solver #Example: - if self.petsc_options is not None: - for k, v in self.petsc_options.items(): + if petsc_options is not None: + for k, v in petsc_options.items(): opts[k] = v opts.prefixPop() ksp.setFromOptions() return ksp - def cost(self,m : dlx.la.Vector) -> float: - temp_petsc_vec_d = dlx.la.create_petsc_vector_wrap(self.mean).copy() temp_petsc_vec_m = dlx.la.create_petsc_vector_wrap(m) temp_petsc_vec_d.axpy(-1., temp_petsc_vec_m) @@ -256,17 +244,13 @@ def cost(self,m : dlx.la.Vector) -> float: temp_petsc_vec_Rd.destroy() return return_value - def grad(self,m : dlx.la.Vector, out : dlx.la.Vector) -> None: temp_petsc_vec_d = dlx.la.create_petsc_vector_wrap(m).copy() temp_petsc_vec_self_mean = dlx.la.create_petsc_vector_wrap(self.mean) temp_petsc_vec_out = dlx.la.create_petsc_vector_wrap(out) - temp_petsc_vec_d.axpy(-1., temp_petsc_vec_self_mean) - self.R.mult(temp_petsc_vec_d,temp_petsc_vec_out) - temp_petsc_vec_d.destroy() temp_petsc_vec_self_mean.destroy() temp_petsc_vec_out.destroy() @@ -274,7 +258,6 @@ def grad(self,m : dlx.la.Vector, out : dlx.la.Vector) -> None: def setLinearizationPoint(self, m : dlx.la.Vector, gauss_newton_approx = False) -> None: return - def __del__(self): self.Msolver.destroy() self.Asolver.destroy() @@ -283,7 +266,7 @@ def __del__(self): self.sqrtM.destroy() -def BiLaplacianPrior(Vh : dlx.fem.FunctionSpace, gamma : float, delta : float, Theta = None, mean=None, rel_tol=1e-12, max_iter=1000, robin_bc=False) -> test_prior: +def BiLaplacianPrior(Vh : dlx.fem.FunctionSpace, gamma : float, delta : float, Theta = None, mean=None, robin_bc=False) -> SqrtPrecisionPDE_Prior: """ This function construct an instance of :code"`SqrtPrecisionPDE_Prior` with covariance matrix :math:`C = (\\delta I + \\gamma \\mbox{div } \\Theta \\nabla) ^ {-2}`. @@ -322,4 +305,4 @@ def sqrt_precision_varf_handler(trial : ufl.TrialFunction, test : ufl.TestFuncti return gamma*varfL + delta*varfM + robin_coeff*varf_robin - return test_prior(Vh, sqrt_precision_varf_handler, mean, rel_tol, max_iter) + return SqrtPrecisionPDE_Prior(Vh, sqrt_precision_varf_handler, mean) From bf55be159412f7f8ac6f346508936ed1739b7558 Mon Sep 17 00:00:00 2001 From: V-Rang Date: Tue, 19 Mar 2024 16:15:13 -0500 Subject: [PATCH 50/50] clean up --- example/poisson_example.py | 1 - example/sfsi_toy_gaussian_reg.py | 1 - hippylibX/algorithms/NewtonCG.py | 2 - hippylibX/algorithms/multivector.py | 218 --------------------------- hippylibX/modeling/PDEProblem.py | 1 - hippylibX/modeling/Regularization.py | 1 - hippylibX/modeling/model.py | 1 - hippylibX/modeling/prior.py | 2 +- 8 files changed, 1 insertion(+), 226 deletions(-) delete mode 100644 hippylibX/algorithms/multivector.py diff --git a/example/poisson_example.py b/example/poisson_example.py index e94e515a..81893c22 100644 --- a/example/poisson_example.py +++ b/example/poisson_example.py @@ -77,7 +77,6 @@ def run_inversion(nx : int, ny : int, noise_variance : float, prior_param : dict pde.solveFwd(u_true,x_true) - # LIKELIHOOD d = dlx.fem.Function(Vh[hpx.STATE]) d.x.array[:] = u_true.array[:] diff --git a/example/sfsi_toy_gaussian_reg.py b/example/sfsi_toy_gaussian_reg.py index 6cb12a9a..c666aa76 100644 --- a/example/sfsi_toy_gaussian_reg.py +++ b/example/sfsi_toy_gaussian_reg.py @@ -44,7 +44,6 @@ def __call__(self, u: dlx.fem.Function, m : dlx.fem.Function, p : dlx.fem.Functi ufl.exp(m)*ufl.inner(u,p)*self.dx + \ .5*ufl.inner(u-self.u0,p)*self.ds - class PACTMisfitForm: def __init__(self, d : float, sigma2 : float): self.sigma2 = sigma2 diff --git a/hippylibX/algorithms/NewtonCG.py b/hippylibX/algorithms/NewtonCG.py index 65601594..e9745468 100644 --- a/hippylibX/algorithms/NewtonCG.py +++ b/hippylibX/algorithms/NewtonCG.py @@ -212,7 +212,6 @@ def _solve_ls(self,x): tolcg = min(cg_coarse_tolerance, math.sqrt(gradnorm/gradnorm_ini)) HessApply = ReducedHessian(self.model) - # solver = CGSolverSteihaug(comm = self.model.prior.R.mpi_comm()) solver = CGSolverSteihaug(comm = self.model.prior.Vh.mesh.comm) solver.set_operator(HessApply) solver.set_preconditioner(self.model.Rsolver()) @@ -339,7 +338,6 @@ def _solve_tr(self,x): tolcg = min(cg_coarse_tolerance, math.sqrt(gradnorm/gradnorm_ini)) HessApply = ReducedHessian(self.model) - # solver = CGSolverSteihaug(comm = self.model.prior.R.mpi_comm()) solver = CGSolverSteihaug(comm = self.model.prior.Vh.mesh.comm) solver.set_operator(HessApply) solver.set_preconditioner(self.model.Rsolver()) diff --git a/hippylibX/algorithms/multivector.py b/hippylibX/algorithms/multivector.py deleted file mode 100644 index e46093cf..00000000 --- a/hippylibX/algorithms/multivector.py +++ /dev/null @@ -1,218 +0,0 @@ -import dolfinx as dlx -from ..utils.vector2function import vector2Function -import numpy as np -import os - -#decorator for functions in classes that are not used -> may not be needed in the final -#version of X -def unused_function(func): - return None - -class MultiVector(): - @unused_function - def dot_v(self, v): - return self.dot(v) - - @unused_function - def dot_mv(self,mv): - shape = (self.nvec(),mv.nvec()) - m = self.dot(mv) - return m.reshape(shape, order='C') - - @unused_function - def Borthogonalize(self,B): - """ - Returns :math:`QR` decomposition of self. :math:`Q` and :math:`R` satisfy the following relations in exact arithmetic - - .. math:: - R \\,= \\,Z, && (1), - - Q^*BQ\\, = \\, I, && (2), - - Q^*BZ \\, = \\,R, && (3), - - ZR^{-1} \\, = \\, Q, && (4). - - Returns: - - :code:`Bq` of type :code:`MultiVector` -> :code:`B`:math:`^{-1}`-orthogonal vectors - :code:`r` of type :code:`ndarray` -> The :math:`r` of the QR decomposition. - - .. note:: :code:`self` is overwritten by :math:`Q`. - """ - return self._mgs_stable(B) - - @unused_function - def orthogonalize(self): - """ - Returns :math:`QR` decomposition of self. :math:`Q` and :math:`R` satisfy the following relations in exact arithmetic - - .. math:: - QR \\, = \\, Z, && (1), - - Q^*Q \\, = \\, I, && (2), - - Q^*Z \\, = \\, R, && (3), - - ZR^{-1} \\, = \\, Q, && (4). - - Returns: - - :code:`r` of type :code:`ndarray` -> The `r` of the QR decomposition - - .. note:: :code:`self` is overwritten by :math:`Q`. - """ - return self._mgs_reortho() - - @unused_function - def _mgs_stable(self, B): - """ - Returns :math:`QR` decomposition of self, which satisfies conditions (1)--(4). - Uses Modified Gram-Schmidt with re-orthogonalization (Rutishauser variant) - for computing the :math:`B`-orthogonal :math:`QR` factorization. - - References: - 1. `A.K. Saibaba, J. Lee and P.K. Kitanidis, Randomized algorithms for Generalized \ - Hermitian Eigenvalue Problems with application to computing \ - Karhunen-Loe've expansion http://arxiv.org/abs/1307.6885` - 2. `W. Gander, Algorithms for the QR decomposition. Res. Rep, 80(02), 1980` - - https://github.com/arvindks/kle - - """ - n = self.nvec() - Bq = MultiVector(self[0], n) - r = np.zeros((n,n), dtype = 'd') - reorth = np.zeros((n,), dtype = 'd') - eps = np.finfo(np.float64).eps - - for k in np.arange(n): - B.mult(self[k], Bq[k]) - t = np.sqrt( Bq[k].inner(self[k])) - - nach = 1; u = 0; - while nach: - u += 1 - for i in np.arange(k): - s = Bq[i].inner(self[k]) - r[i,k] += s - self[k].axpy(-s, self[i]) - - B.mult(self[k], Bq[k]) - tt = np.sqrt(Bq[k].inner(self[k])) - if tt > t*10.*eps and tt < t/10.: - nach = 1; t = tt; - else: - nach = 0; - if tt < 10.*eps*t: - tt = 0. - - - reorth[k] = u - r[k,k] = tt - if np.abs(tt*eps) > 0.: - tt = 1./tt - else: - tt = 0. - - self.scale(k, tt) - Bq.scale(k, tt) - - return Bq, r - - @unused_function - def _mgs_reortho(self): - n = self.nvec() - r = np.zeros((n,n), dtype = 'd') - reorth = np.zeros((n,), dtype = 'd') - eps = np.finfo(np.float64).eps - - for k in np.arange(n): - t = np.sqrt( self[k].inner(self[k])) - - nach = 1; u = 0; - while nach: - u += 1 - for i in np.arange(k): - s = self[i].inner(self[k]) - r[i,k] += s - self[k].axpy(-s, self[i]) - - tt = np.sqrt(self[k].inner(self[k])) - if tt > t*10.*eps and tt < t/10.: - nach = 1; t = tt; - else: - nach = 0; - if tt < 10.*eps*t: - tt = 0. - - - reorth[k] = u - r[k,k] = tt - if np.abs(tt*eps) > 0.: - tt = 1./tt - else: - tt = 0. - - self.scale(k, tt) - - return r - - @unused_function - def export(self, Vh, filename, varname = "mv", normalize=False): - """ - Export in paraview this multivector. - - Inputs: - - - :code:`Vh`: the parameter finite element space. - - :code:`filename`: the name of the paraview output file. - - :code:`varname`: the name of the paraview variable. - - :code:`normalize`: if :code:`True` the vector is rescaled such that :math:`\\| u \\|_{\\infty} = 1.` - """ - if '.xdmf' in filename: - self._exportXDMF(Vh, filename, varname, normalize) - else: - self._exportFile(Vh, filename, varname, normalize) - - @unused_function - def _exportXDMF(self, Vh, filename, varname, normalize): - """ - Specialization of export using dl.File - """ - fid = dl.XDMFFile(Vh.mesh().mpi_comm(), filename) - fid.parameters["functions_share_mesh"] = True - fid.parameters["rewrite_function_mesh"] = False - - fun = dl.Function(Vh, name = varname) - - if not normalize: - for i in range(self.nvec()): - fun.vector().zero() - fun.vector().axpy(1., self[i]) - fid.write(fun,i) - else: - for i in range(self.nvec()): - s = self[i].norm("linf") - fun.vector().zero() - fun.vector().axpy(1./s, self[i]) - fid.write(fun,i) - - @unused_function - def _exportFile(self, Vh, filename, varname, normalize): - """ - Specialization of export using dl.File - """ - fid = dl.File(filename) - fun = dl.Function(Vh, name = varname) - if not normalize: - for i in range(self.nvec()): - fun.vector().zero() - fun.vector().axpy(1., self[i]) - fid << fun - else: - for i in range(self.nvec()): - s = self[i].norm("linf") - fun.vector().zero() - fun.vector().axpy(1./s, self[i]) - fid << fun diff --git a/hippylibX/modeling/PDEProblem.py b/hippylibX/modeling/PDEProblem.py index de7937cb..bd7114c1 100644 --- a/hippylibX/modeling/PDEProblem.py +++ b/hippylibX/modeling/PDEProblem.py @@ -3,7 +3,6 @@ import ufl import petsc4py from .variables import STATE, PARAMETER, ADJOINT -# from ..algorithms.linSolvers import PETScLUSolver from ..utils.vector2function import vector2Function, updateFromVector import numpy as np diff --git a/hippylibX/modeling/Regularization.py b/hippylibX/modeling/Regularization.py index 570828a6..a7bf715d 100644 --- a/hippylibX/modeling/Regularization.py +++ b/hippylibX/modeling/Regularization.py @@ -59,7 +59,6 @@ def _createsolver(self,petsc_options) -> petsc4py.PETSc.KSP: return ksp - def cost(self,m : dlx.la.Vector): hpx.updateFromVector(self.mfun,m) cost_functional = self.functional_handler(self.mfun) diff --git a/hippylibX/modeling/model.py b/hippylibX/modeling/model.py index 59f842d5..0ca52d6e 100644 --- a/hippylibX/modeling/model.py +++ b/hippylibX/modeling/model.py @@ -88,7 +88,6 @@ def init_parameter(self, m) -> dlx.la.Vector: """ Reshape :code:`m` so that it is compatible with the parameter variable """ - # return self.prior.generate_parameter(0) return self.problem.generate_parameter() diff --git a/hippylibX/modeling/prior.py b/hippylibX/modeling/prior.py index ad8c0062..5b5204cf 100644 --- a/hippylibX/modeling/prior.py +++ b/hippylibX/modeling/prior.py @@ -210,7 +210,7 @@ def sample(self, noise : dlx.la.Vector, s : dlx.la.Vector, add_mean=True) -> Non temp_petsc_vec_s.destroy() - def _createsolver(self,petsc_options) -> petsc4py.PETSc.KSP: + def _createsolver(self, petsc_options) -> petsc4py.PETSc.KSP: ksp = petsc4py.PETSc.KSP().create(self.Vh.mesh.comm) problem_prefix = f"dolfinx_solve_{id(self)}" ksp.setOptionsPrefix(problem_prefix)