From aa4e0e3e53eb024740c10b2656797c5b00f9b27a Mon Sep 17 00:00:00 2001 From: houpc Date: Thu, 30 Oct 2025 18:23:06 +0800 Subject: [PATCH 1/7] update readme and license --- README.md | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++ license.md | 7 +++ 2 files changed, 148 insertions(+) create mode 100644 license.md diff --git a/README.md b/README.md index bccdd05..2e7618c 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,144 @@ [![alpha](https://img.shields.io/badge/docs-alpha-blue.svg)](https://numericaleft.github.io/MCintegration.py/) [![Build Status](https://github.com/numericalEFT/MCIntegration.py/workflows/CI/badge.svg)](https://github.com/numericalEFT/MCIntegration.py/actions) [![codecov](https://codecov.io/gh/numericalEFT/MCintegration.py/graph/badge.svg?token=851N2CNOTN)](https://codecov.io/gh/numericalEFT/MCintegration.py) +A Python library for Monte Carlo integration with support for multi-CPU and GPU computations. + +## Overview + +MCintegration is a specialized library designed for numerical integration using Monte Carlo methods. It provides efficient implementations of various integration algorithms with focus on applications in computational physics and effective field theories (EFT). + +The library offers: +- Multiple Monte Carlo integration algorithms +- Support for multi-CPU parallelization +- GPU acceleration capabilities +- Integration with PyTorch for tensor-based computations + +## Installation + +```bash +pip install mcintegration +``` + +Or install from source: + +```bash +python setup.py install +``` + +## Usage + +### Example 1: Unit Circle Integration + +This example demonstrates different Monte Carlo methods for integrating functions over [-1,1]×[-1,1]: + +```python +from MCintegration import MonteCarlo, MarkovChainMonteCarlo, Vegas +import torch + +# Define integrand function +def unit_circle(x, f): + r2 = x[:, 0]**2 + x[:, 1]**2 + f[:, 0] = (r2 <= 1).float() + return f.mean(dim=-1) + +# Set up integration parameters +dim = 2 +bounds = [(-1, 1)] * dim +n_eval = 6400000 +batch_size = 10000 +n_therm = 100 + +# Create integrator instances +mc = MonteCarlo(f=unit_circle, bounds=bounds, batch_size=batch_size) +mcmc = MarkovChainMonteCarlo(f=unit_circle, bounds=bounds, batch_size=batch_size, nburnin=n_therm) + +# Perform integration +result_mc = mc(n_eval) +result_mcmc = mcmc(n_eval) +``` + +### Example 2: Singular Function Integration + +This example shows integration of a function with a singularity at x=0: + +```python +# Integrate log(x)/sqrt(x) which has a singularity at x=0 +def singular_func(x, f): + f[:, 0] = torch.log(x[:, 0]) / torch.sqrt(x[:, 0]) + return f[:, 0] + +# Set up integration parameters +dim = 1 +bounds = [(0, 1)] +n_eval = 6400000 +batch_size = 10000 +n_therm = 100 + +# Use VEGAS algorithm which adapts to the singular structure +vegas_map = Vegas(dim, ninc=1000) +vegas_map.adaptive_training(batch_size, singular_func) + +# Create integrator instances using the adapted vegas map +vegas_mc = MonteCarlo(f=singular_func, bounds=bounds, batch_size=batch_size, maps=vegas_map) +vegas_mcmc = MarkovChainMonteCarlo(f=singular_func, bounds=bounds, batch_size=batch_size, nburnin=n_therm, maps=vegas_map) + +# Perform integration +result_vegas = vegas_mc(n_eval) +result_vegas_mcmc = vegas_mcmc(n_eval) +``` + +### Example 3: Multiple Sharp Peak Integrands in Higher Dimensions + +This example demonstrates integration of a sharp Gaussian peak and its moments in 4D space: + +```python +# Define a sharp peak and its moments integrands +# This represents a Gaussian peak centered at (0.5, 0.5, 0.5, 0.5) +def sharp_integrands(x, f): + f[:, 0] = torch.sum((x - 0.5) ** 2, dim=-1) # Distance from center + f[:, 0] *= -200 # Scale by width parameter + f[:, 0].exp_() # Exponentiate to create Gaussian + f[:, 1] = f[:, 0] * x[:, 0] # First moment + f[:, 2] = f[:, 0] * x[:, 0] ** 2 # Second moment + return f.mean(dim=-1) + +# Set up 4D integration with sharp peak +dim = 4 +bounds = [(0, 1)] * dim +n_eval = 6400000 +batch_size = 10000 +n_therm = 100 + +# Use VEGAS algorithm which adapts to the peak structure +vegas_map = Vegas(dim, ninc=1000) +vegas_map.adaptive_training(batch_size, sharp_integrands, f_dim=3) + +# Create integrator instances using the adapted vegas map +vegas_mc = MonteCarlo(f=sharp_integrands, f_dim=3, bounds=bounds, batch_size=batch_size, maps=vegas_map) +vegas_mcmc = MarkovChainMonteCarlo(f=sharp_integrands, f_dim=3, bounds=bounds, batch_size=batch_size, nburnin=n_therm, maps=vegas_map) + +# Perform integration +result_vegas = vegas_mc(n_eval) +result_vegas_mcmc = vegas_mcmc(n_eval) +``` + +## Features + +- **Base integration methods**: Core Monte Carlo algorithms in `MCintegration/base.py` +- **Integrator implementations**: Various MC integration strategies in `MCintegration/integrators.py` +- **Variable transformations**: Coordinate mapping utilities in `MCintegration/maps.py` +- **Utility functions**: Helper functions for numerical computations in `MCintegration/utils.py` +- **Multi-CPU support**: Parallel processing capabilities demonstrated in `MCintegration/mc_multicpu_test.py` +- **GPU acceleration**: CUDA-enabled functions through PyTorch in the examples directory + + +## Requirements + +- Python 3.7+ +- NumPy +- PyTorch +- gvar + +## Acknowledgements and Related Packages +The development of `MCIntegration.py` has been greatly inspired and influenced by `vegas` package. We would like to express our appreciation to the following: +- [vegas](https://github.com/gplepage/vegas) A Python package offering Monte Carlo estimations of multidimensional integrals, with notable improvements on the original Vegas algorithm. It's been a valuable reference for us. Learn more from the vegas [documentation](https://vegas.readthedocs.io/). **Reference: G. P. Lepage, J. Comput. Phys. 27, 192 (1978) and G. P. Lepage, J. Comput. Phys. 439, 110386 (2021) [arXiv:2009.05112](https://arxiv.org/abs/2009.05112)**. \ No newline at end of file diff --git a/license.md b/license.md new file mode 100644 index 0000000..b964bdf --- /dev/null +++ b/license.md @@ -0,0 +1,7 @@ +Copyright (c) 2025: Pengcheng Hou, Tao Wang, Caiyu Fan, and Kun Chen. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From b512a03dbba7a4b77b1c72dc2b7c3790f22d4843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=83=E6=89=8D=E6=B8=9D?= Date: Thu, 30 Oct 2025 21:31:38 +0800 Subject: [PATCH 2/7] fix codecov --- codecov.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codecov.yml b/codecov.yml index 7cdebbd..24cf127 100644 --- a/codecov.yml +++ b/codecov.yml @@ -8,3 +8,5 @@ coverage: project: default: target: 95% +fixes: + - "MCintegration.py/MCintegration.py/::MCintegration.py/" From 07d353b44c571c744532ce19f35f1feac991211b Mon Sep 17 00:00:00 2001 From: fancaiyu Date: Fri, 31 Oct 2025 11:22:11 +0800 Subject: [PATCH 3/7] fix merge covergae --- .github/workflows/CI.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3827e33..2941caf 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -37,11 +37,10 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install coverage - pip install pytest pytest-cov + pip install coverage pytest pytest-cov pip install -r requirements.txt - - name: Run tests + - name: Run tests with multiprocessing coverage run: | export RANK=0 export LOCAL_RANK=0 @@ -49,7 +48,13 @@ jobs: export MASTER_ADDR=localhost export MASTER_PORT=12345 export PYTHONPATH=MCintegration - pytest --cov --cov-report=xml --ignore=examples + + # 使用 coverage 并开启 parallel 模式 + coverage run --parallel-mode -m pytest --cov=MCintegration --ignore=examples + # 合并多进程结果 + coverage combine + coverage xml + coverage report -m - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 From 1ec11b3f659e1d4d2e51b4e24a76c5cbc6c9cb5d Mon Sep 17 00:00:00 2001 From: fancaiyu Date: Fri, 31 Oct 2025 11:30:38 +0800 Subject: [PATCH 4/7] fix coverage --- .github/workflows/CI.yml | 13 ++++--------- MCintegration/mc_multicpu_test.py | 4 ++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2941caf..3827e33 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -37,10 +37,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install coverage pytest pytest-cov + pip install coverage + pip install pytest pytest-cov pip install -r requirements.txt - - name: Run tests with multiprocessing coverage + - name: Run tests run: | export RANK=0 export LOCAL_RANK=0 @@ -48,13 +49,7 @@ jobs: export MASTER_ADDR=localhost export MASTER_PORT=12345 export PYTHONPATH=MCintegration - - # 使用 coverage 并开启 parallel 模式 - coverage run --parallel-mode -m pytest --cov=MCintegration --ignore=examples - # 合并多进程结果 - coverage combine - coverage xml - coverage report -m + pytest --cov --cov-report=xml --ignore=examples - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 diff --git a/MCintegration/mc_multicpu_test.py b/MCintegration/mc_multicpu_test.py index 1be7b9f..c1b297c 100644 --- a/MCintegration/mc_multicpu_test.py +++ b/MCintegration/mc_multicpu_test.py @@ -79,6 +79,10 @@ def two_integrands(x, f): if dist.is_initialized(): dist.destroy_process_group() +def test_mcmc_singlethread(): + # 直接在当前进程初始化并运行,避免 mp.spawn 启动子进程 + world_size = 1 + init_process(rank=0, world_size=world_size, fn=run_mcmc, backend=backend) def test_mcmc(world_size=2): # Use fewer processes than CPU cores to avoid resource contention From 231bad6dc010a6148ab803442ed6d07d32460aed Mon Sep 17 00:00:00 2001 From: fancaiyu Date: Fri, 31 Oct 2025 11:37:37 +0800 Subject: [PATCH 5/7] fix coverage --- MCintegration/mc_multicpu_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCintegration/mc_multicpu_test.py b/MCintegration/mc_multicpu_test.py index c1b297c..2c605b3 100644 --- a/MCintegration/mc_multicpu_test.py +++ b/MCintegration/mc_multicpu_test.py @@ -86,7 +86,7 @@ def test_mcmc_singlethread(): def test_mcmc(world_size=2): # Use fewer processes than CPU cores to avoid resource contention - world_size = min(world_size, mp.cpu_count()) + # world_size = min(world_size, mp.cpu_count()) print(f"Starting with {world_size} processes") # Start processes with proper error handling From d1601d69c194c168db397fe2c9db3869a25d4a97 Mon Sep 17 00:00:00 2001 From: fancaiyu Date: Fri, 31 Oct 2025 11:46:23 +0800 Subject: [PATCH 6/7] fix --- MCintegration/integrators_test.py | 75 +++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/MCintegration/integrators_test.py b/MCintegration/integrators_test.py index b0ebb33..c332c0a 100644 --- a/MCintegration/integrators_test.py +++ b/MCintegration/integrators_test.py @@ -417,6 +417,81 @@ def test_distributed_initialization(self): self.assertEqual(integrator.rank, 0) self.assertEqual(integrator.world_size, 1) + def test_statistics_worldsize_gt1(self): + """Mock 分布式 gather 测试 world_size > 1 分支覆盖""" + + bounds = torch.tensor([[0.0, 1.0]], dtype=torch.float64) + f = lambda x, fx: fx.copy_(x) # 不重要,只是占位 + integrator = Integrator(bounds=bounds, f=f) + integrator.world_size = 2 + integrator.rank = 0 + + means = torch.ones((2, 1)) + vars = torch.ones((2, 1)) * 0.5 + + # ---- 构造假的 dist 模块 ---- + class DummyDist: + def gather(self, tensor, gather_list=None, dst=0): + # 模拟 rank 0 收到两份数据 + if gather_list is not None: + gather_list[0].copy_(tensor) + gather_list[1].copy_(tensor * 2) + + def get_rank(self): + return integrator.rank + + def get_world_size(self): + return integrator.world_size + + def is_initialized(self): + return True + + import MCintegration.integrators as integrators_module + orig_dist = integrators_module.dist + integrators_module.dist = DummyDist() + + try: + result = integrator.statistics(means, vars, neval=100) + self.assertIsNotNone(result) + self.assertTrue(hasattr(result, "__len__")) + finally: + # 恢复原 dist + integrators_module.dist = orig_dist + + def test_statistics_worldsize_gt1_rank1(self): + """Mock 分布式测试 rank != 0 分支覆盖""" + + bounds = torch.tensor([[0.0, 1.0]], dtype=torch.float64) + f = lambda x, fx: fx.copy_(x) + integrator = Integrator(bounds=bounds, f=f) + integrator.world_size = 2 + integrator.rank = 1 + + means = torch.ones((2, 1)) + vars = torch.ones((2, 1)) * 0.5 + + class DummyDist: + def gather(self, tensor, gather_list=None, dst=0): + pass # rank!=0 的情况 + + def get_rank(self): + return integrator.rank + + def get_world_size(self): + return integrator.world_size + + def is_initialized(self): + return True + + import MCintegration.integrators as integrators_module + orig_dist = integrators_module.dist + integrators_module.dist = DummyDist() + + try: + result = integrator.statistics(means, vars, neval=100) + self.assertIsNone(result) + finally: + integrators_module.dist = orig_dist # @unittest.skipIf(not torch.distributed.is_available(), "Distributed not available") # def test_multi_gpu_consistency(self): # if torch.cuda.device_count() >= 2: From bb236aaf2cb985f3ed800b3da96dc0c7bf9f223b Mon Sep 17 00:00:00 2001 From: fancaiyu Date: Fri, 31 Oct 2025 11:58:05 +0800 Subject: [PATCH 7/7] try --- MCintegration/integrators_test.py | 108 +++++------------------------- 1 file changed, 16 insertions(+), 92 deletions(-) diff --git a/MCintegration/integrators_test.py b/MCintegration/integrators_test.py index c332c0a..99d9a50 100644 --- a/MCintegration/integrators_test.py +++ b/MCintegration/integrators_test.py @@ -416,102 +416,26 @@ def test_distributed_initialization(self): integrator = Integrator(bounds=bounds, f=f) self.assertEqual(integrator.rank, 0) self.assertEqual(integrator.world_size, 1) + @unittest.skipIf(not torch.distributed.is_available(), "Distributed not available") + def test_multi_gpu_consistency(self): + if torch.cuda.device_count() >= 2: + bounds = torch.tensor([[0.0, 1.0]], dtype=torch.float64) + f = lambda x, fx: torch.ones_like(x) - def test_statistics_worldsize_gt1(self): - """Mock 分布式 gather 测试 world_size > 1 分支覆盖""" - - bounds = torch.tensor([[0.0, 1.0]], dtype=torch.float64) - f = lambda x, fx: fx.copy_(x) # 不重要,只是占位 - integrator = Integrator(bounds=bounds, f=f) - integrator.world_size = 2 - integrator.rank = 0 - - means = torch.ones((2, 1)) - vars = torch.ones((2, 1)) * 0.5 - - # ---- 构造假的 dist 模块 ---- - class DummyDist: - def gather(self, tensor, gather_list=None, dst=0): - # 模拟 rank 0 收到两份数据 - if gather_list is not None: - gather_list[0].copy_(tensor) - gather_list[1].copy_(tensor * 2) - - def get_rank(self): - return integrator.rank - - def get_world_size(self): - return integrator.world_size - - def is_initialized(self): - return True - - import MCintegration.integrators as integrators_module - orig_dist = integrators_module.dist - integrators_module.dist = DummyDist() + # Create two integrators on different devices + integrator1 = Integrator(bounds=bounds, f=f, device="cuda:0") + integrator2 = Integrator(bounds=bounds, f=f, device="cuda:1") - try: - result = integrator.statistics(means, vars, neval=100) - self.assertIsNotNone(result) - self.assertTrue(hasattr(result, "__len__")) - finally: - # 恢复原 dist - integrators_module.dist = orig_dist + # Results should be consistent across devices + result1 = integrator1(neval=10000) + result2 = integrator2(neval=10000) - def test_statistics_worldsize_gt1_rank1(self): - """Mock 分布式测试 rank != 0 分支覆盖""" + if hasattr(result1, "mean"): + value1, value2 = result1.mean, result2.mean + else: + value1, value2 = result1, result2 - bounds = torch.tensor([[0.0, 1.0]], dtype=torch.float64) - f = lambda x, fx: fx.copy_(x) - integrator = Integrator(bounds=bounds, f=f) - integrator.world_size = 2 - integrator.rank = 1 - - means = torch.ones((2, 1)) - vars = torch.ones((2, 1)) * 0.5 - - class DummyDist: - def gather(self, tensor, gather_list=None, dst=0): - pass # rank!=0 的情况 - - def get_rank(self): - return integrator.rank - - def get_world_size(self): - return integrator.world_size - - def is_initialized(self): - return True - - import MCintegration.integrators as integrators_module - orig_dist = integrators_module.dist - integrators_module.dist = DummyDist() - - try: - result = integrator.statistics(means, vars, neval=100) - self.assertIsNone(result) - finally: - integrators_module.dist = orig_dist - # @unittest.skipIf(not torch.distributed.is_available(), "Distributed not available") - # def test_multi_gpu_consistency(self): - # if torch.cuda.device_count() >= 2: - # bounds = torch.tensor([[0.0, 1.0]], dtype=torch.float64) - # f = lambda x, fx: torch.ones_like(x) - - # # Create two integrators on different devices - # integrator1 = Integrator(bounds=bounds, f=f, device="cuda:0") - # integrator2 = Integrator(bounds=bounds, f=f, device="cuda:1") - - # # Results should be consistent across devices - # result1 = integrator1(neval=10000) - # result2 = integrator2(neval=10000) - - # if hasattr(result1, "mean"): - # value1, value2 = result1.mean, result2.mean - # else: - # value1, value2 = result1, result2 - - # self.assertAlmostEqual(float(value1), float(value2), places=1) + self.assertAlmostEqual(float(value1), float(value2), places=1) if __name__ == "__main__":