From 3be926032f2d27024c9670d7ec5feae4a8261913 Mon Sep 17 00:00:00 2001 From: noman404 Date: Thu, 23 Jan 2025 08:04:46 +0800 Subject: [PATCH 01/21] running on python 3.13.0 --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 59b2700f..5d5531a6 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ general_requirements = [ "pytest>=8,<9", - "numpy~=1.26.4", + "numpy~=2.0.0", "sortedcontainers<3", "numexpr<3", "dpath<3", @@ -60,6 +60,7 @@ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering :: Information Analysis", ], description="Core microsimulation engine enabling country-specific policy models.", From 071b558e65cbe9687d57fd39ed2a69fd3fd2a6a5 Mon Sep 17 00:00:00 2001 From: noman404 Date: Thu, 23 Jan 2025 18:15:15 +0800 Subject: [PATCH 02/21] upgrade numpy 2.1.0 --- Makefile | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 33da57ef..9120f20a 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,8 @@ format: install: pip install -e ".[dev]" --config-settings editable_mode=compat - pip install policyengine-us - pip install policyengine-uk +# pip install policyengine-us +# pip install policyengine-uk test-country-template: policyengine-core test policyengine_core/country_template/tests -c policyengine_core.country_template diff --git a/setup.py b/setup.py index 5d5531a6..e6cabfc9 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ general_requirements = [ "pytest>=8,<9", - "numpy~=2.0.0", + "numpy~=2.1.0", "sortedcontainers<3", "numexpr<3", "dpath<3", From 464a21dd9781695747ef92b913dd47ed22850c0d Mon Sep 17 00:00:00 2001 From: noman404 Date: Thu, 23 Jan 2025 18:17:45 +0800 Subject: [PATCH 03/21] enable policyengine dependencies for build test --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9120f20a..33da57ef 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,8 @@ format: install: pip install -e ".[dev]" --config-settings editable_mode=compat -# pip install policyengine-us -# pip install policyengine-uk + pip install policyengine-us + pip install policyengine-uk test-country-template: policyengine-core test policyengine_core/country_template/tests -c policyengine_core.country_template From 593bc874222f03d032fd09949026035e6ea3a341 Mon Sep 17 00:00:00 2001 From: noman404 Date: Sun, 26 Jan 2025 22:18:26 +0800 Subject: [PATCH 04/21] updated to float64, round and inf --- policyengine_core/populations/group_population.py | 4 ++-- policyengine_core/taxscales/marginal_rate_tax_scale.py | 8 ++++---- policyengine_core/taxscales/rate_tax_scale_like.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/policyengine_core/populations/group_population.py b/policyengine_core/populations/group_population.py index e201d0f5..fd6b1bee 100644 --- a/policyengine_core/populations/group_population.py +++ b/policyengine_core/populations/group_population.py @@ -232,7 +232,7 @@ def max(self, array: ArrayLike, role: Role = None) -> ArrayLike: return self.reduce( array, reducer=numpy.maximum, - neutral_element=-numpy.infty, + neutral_element=-numpy.inf, role=role, ) @@ -256,7 +256,7 @@ def min(self, array: ArrayLike, role: Role = None) -> ArrayLike: return self.reduce( array, reducer=numpy.minimum, - neutral_element=numpy.infty, + neutral_element=numpy.inf, role=role, ) diff --git a/policyengine_core/taxscales/marginal_rate_tax_scale.py b/policyengine_core/taxscales/marginal_rate_tax_scale.py index 2da91c53..3453f019 100644 --- a/policyengine_core/taxscales/marginal_rate_tax_scale.py +++ b/policyengine_core/taxscales/marginal_rate_tax_scale.py @@ -66,12 +66,12 @@ def calc( # # numpy.finfo(float_).eps thresholds1 = numpy.outer( - factor + numpy.finfo(numpy.float_).eps, + factor + numpy.finfo(numpy.float64).eps, numpy.array(self.thresholds + [numpy.inf]), ) if round_base_decimals is not None: - thresholds1 = numpy.round_(thresholds1, round_base_decimals) + thresholds1 = numpy.round(thresholds1, round_base_decimals) a = numpy.maximum( numpy.minimum(base1, thresholds1[:, 1:]) - thresholds1[:, :-1], 0 @@ -82,8 +82,8 @@ def calc( else: r = numpy.tile(self.rates, (len(tax_base), 1)) - b = numpy.round_(a, round_base_decimals) - return numpy.round_(r * b, round_base_decimals).sum(axis=1) + b = numpy.round(a, round_base_decimals) + return numpy.round(r * b, round_base_decimals).sum(axis=1) def combine_bracket( self, diff --git a/policyengine_core/taxscales/rate_tax_scale_like.py b/policyengine_core/taxscales/rate_tax_scale_like.py index b598f447..79d39728 100644 --- a/policyengine_core/taxscales/rate_tax_scale_like.py +++ b/policyengine_core/taxscales/rate_tax_scale_like.py @@ -175,12 +175,12 @@ def bracket_indices( # # numpy.finfo(float_).eps thresholds1 = numpy.outer( - +factor + numpy.finfo(numpy.float_).eps, + +factor + numpy.finfo(numpy.float64).eps, numpy.array(self.thresholds), ) if round_decimals is not None: - thresholds1 = numpy.round_(thresholds1, round_decimals) + thresholds1 = numpy.round(thresholds1, round_decimals) return (base1 - thresholds1 >= 0).sum(axis=1) - 1 From 1451a32f477fba1de574adcea6dfdd531847d998 Mon Sep 17 00:00:00 2001 From: noman404 Date: Sun, 26 Jan 2025 23:52:54 +0800 Subject: [PATCH 05/21] updated numpy.select default types --- .../parameters/vectorial_parameter_node_at_instant.py | 3 ++- policyengine_core/tools/simulation_dumper.py | 3 ++- tests/core/parameters_fancy_indexing/test_fancy_indexing.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/policyengine_core/parameters/vectorial_parameter_node_at_instant.py b/policyengine_core/parameters/vectorial_parameter_node_at_instant.py index dbd1d14b..8d220df4 100644 --- a/policyengine_core/parameters/vectorial_parameter_node_at_instant.py +++ b/policyengine_core/parameters/vectorial_parameter_node_at_instant.py @@ -203,13 +203,14 @@ def __getitem__(self, key: str) -> Any: enum = type(key[0]) key = numpy.select( [key == item for item in enum], - [item.name for item in enum], + [str(item.name) for item in enum], ) elif isinstance(key, EnumArray): enum = key.possible_values key = numpy.select( [key == item.index for item in enum], [item.name for item in enum], + default="unknown" ) else: key = key.astype("str") diff --git a/policyengine_core/tools/simulation_dumper.py b/policyengine_core/tools/simulation_dumper.py index dd0c0ad6..1a1d6529 100644 --- a/policyengine_core/tools/simulation_dumper.py +++ b/policyengine_core/tools/simulation_dumper.py @@ -95,7 +95,8 @@ def _dump_entity(population, directory): else: encoded_roles = np.select( [population.members_role == role for role in flattened_roles], - [role.key for role in flattened_roles], + [str(role.key) for role in flattened_roles], + default="unknown" ) np.save(os.path.join(path, "members_role.npy"), encoded_roles) diff --git a/tests/core/parameters_fancy_indexing/test_fancy_indexing.py b/tests/core/parameters_fancy_indexing/test_fancy_indexing.py index d753586f..33904903 100644 --- a/tests/core/parameters_fancy_indexing/test_fancy_indexing.py +++ b/tests/core/parameters_fancy_indexing/test_fancy_indexing.py @@ -114,5 +114,5 @@ class TypesZone(Enum): z1 = "Zone 1" z2 = "Zone 2" - zone = np.asarray([TypesZone.z1, TypesZone.z2, TypesZone.z2, TypesZone.z1]) + zone = np.asarray([z.name for z in [TypesZone.z1, TypesZone.z2, TypesZone.z2, TypesZone.z1]]) assert_near(P.single.owner[zone], [100, 200, 200, 100]) From 5405f6a663a764b674c11aa44948e55d21327976 Mon Sep 17 00:00:00 2001 From: noman404 Date: Mon, 27 Jan 2025 00:14:57 +0800 Subject: [PATCH 06/21] fix concat issue --- policyengine_core/commons/formulas.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/policyengine_core/commons/formulas.py b/policyengine_core/commons/formulas.py index 4695cf9c..7d316c36 100644 --- a/policyengine_core/commons/formulas.py +++ b/policyengine_core/commons/formulas.py @@ -92,6 +92,11 @@ def concat(this: ArrayLike[str], that: ArrayLike[str]) -> ArrayType[str]: array(['this1.0', 'that2.5']...) """ + if isinstance(this, tuple): + raise TypeError("First argument must not be a tuple.") + + if isinstance(that, tuple): + raise TypeError("Second argument must not be a tuple.") if isinstance(this, numpy.ndarray) and not numpy.issubdtype( this.dtype, numpy.str_ From db0296543e3236c9bb1c608b3fb20ac3ba981efd Mon Sep 17 00:00:00 2001 From: noman404 Date: Mon, 27 Jan 2025 00:16:00 +0800 Subject: [PATCH 07/21] fix enum passing issue --- policyengine_core/enums/enum_array.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/policyengine_core/enums/enum_array.py b/policyengine_core/enums/enum_array.py index 1bf5148b..c468a2c7 100644 --- a/policyengine_core/enums/enum_array.py +++ b/policyengine_core/enums/enum_array.py @@ -96,7 +96,8 @@ def decode_to_str(self) -> numpy.str_: """ return numpy.select( [self == item.index for item in self.possible_values], - [item.name for item in self.possible_values], + [str(item.name) for item in self.possible_values], + default="unknown" ) def __repr__(self) -> str: From 9fdd05195cb8b92515f840ce04408399a68f5d9e Mon Sep 17 00:00:00 2001 From: noman404 Date: Mon, 27 Jan 2025 00:16:19 +0800 Subject: [PATCH 08/21] fix tracer line issue --- tests/core/test_tracers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/test_tracers.py b/tests/core/test_tracers.py index 266b7982..c538346a 100644 --- a/tests/core/test_tracers.py +++ b/tests/core/test_tracers.py @@ -403,7 +403,7 @@ def test_log_aggregate(tracer): lines = tracer.computation_log.lines(aggregate=True) assert ( - lines[0] == " A<2017, (default)> = {'avg': 1.0, 'max': 1, 'min': 1}" + lines[0].strip() == "A<2017, (default)> = {'avg': np.float64(1.0), 'max': np.int64(1), 'min': np.int64(1)}" ) From f5b94d415b7d834095aba06e3444ddee4bc4eba3 Mon Sep 17 00:00:00 2001 From: noman404 Date: Thu, 30 Jan 2025 00:22:53 +0800 Subject: [PATCH 09/21] fix unknow dtype issue for PE us --- .../parameters/vectorial_parameter_node_at_instant.py | 1 + 1 file changed, 1 insertion(+) diff --git a/policyengine_core/parameters/vectorial_parameter_node_at_instant.py b/policyengine_core/parameters/vectorial_parameter_node_at_instant.py index 8d220df4..3dc3d25f 100644 --- a/policyengine_core/parameters/vectorial_parameter_node_at_instant.py +++ b/policyengine_core/parameters/vectorial_parameter_node_at_instant.py @@ -204,6 +204,7 @@ def __getitem__(self, key: str) -> Any: key = numpy.select( [key == item for item in enum], [str(item.name) for item in enum], + default="unknown" ) elif isinstance(key, EnumArray): enum = key.possible_values From 41a30bdd0aa0d0634626cb8da39bfd7159457cf0 Mon Sep 17 00:00:00 2001 From: noman404 Date: Thu, 30 Jan 2025 02:46:33 +0800 Subject: [PATCH 10/21] applied format --- policyengine_core/enums/enum_array.py | 2 +- .../parameters/vectorial_parameter_node_at_instant.py | 4 ++-- policyengine_core/tools/simulation_dumper.py | 2 +- .../core/parameters_fancy_indexing/test_fancy_indexing.py | 7 ++++++- tests/core/test_tracers.py | 3 ++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/policyengine_core/enums/enum_array.py b/policyengine_core/enums/enum_array.py index c468a2c7..c518add8 100644 --- a/policyengine_core/enums/enum_array.py +++ b/policyengine_core/enums/enum_array.py @@ -97,7 +97,7 @@ def decode_to_str(self) -> numpy.str_: return numpy.select( [self == item.index for item in self.possible_values], [str(item.name) for item in self.possible_values], - default="unknown" + default="unknown", ) def __repr__(self) -> str: diff --git a/policyengine_core/parameters/vectorial_parameter_node_at_instant.py b/policyengine_core/parameters/vectorial_parameter_node_at_instant.py index 3dc3d25f..0b50d928 100644 --- a/policyengine_core/parameters/vectorial_parameter_node_at_instant.py +++ b/policyengine_core/parameters/vectorial_parameter_node_at_instant.py @@ -204,14 +204,14 @@ def __getitem__(self, key: str) -> Any: key = numpy.select( [key == item for item in enum], [str(item.name) for item in enum], - default="unknown" + default="unknown", ) elif isinstance(key, EnumArray): enum = key.possible_values key = numpy.select( [key == item.index for item in enum], [item.name for item in enum], - default="unknown" + default="unknown", ) else: key = key.astype("str") diff --git a/policyengine_core/tools/simulation_dumper.py b/policyengine_core/tools/simulation_dumper.py index 1a1d6529..4e2f8a18 100644 --- a/policyengine_core/tools/simulation_dumper.py +++ b/policyengine_core/tools/simulation_dumper.py @@ -96,7 +96,7 @@ def _dump_entity(population, directory): encoded_roles = np.select( [population.members_role == role for role in flattened_roles], [str(role.key) for role in flattened_roles], - default="unknown" + default="unknown", ) np.save(os.path.join(path, "members_role.npy"), encoded_roles) diff --git a/tests/core/parameters_fancy_indexing/test_fancy_indexing.py b/tests/core/parameters_fancy_indexing/test_fancy_indexing.py index 33904903..476a4f38 100644 --- a/tests/core/parameters_fancy_indexing/test_fancy_indexing.py +++ b/tests/core/parameters_fancy_indexing/test_fancy_indexing.py @@ -114,5 +114,10 @@ class TypesZone(Enum): z1 = "Zone 1" z2 = "Zone 2" - zone = np.asarray([z.name for z in [TypesZone.z1, TypesZone.z2, TypesZone.z2, TypesZone.z1]]) + zone = np.asarray( + [ + z.name + for z in [TypesZone.z1, TypesZone.z2, TypesZone.z2, TypesZone.z1] + ] + ) assert_near(P.single.owner[zone], [100, 200, 200, 100]) diff --git a/tests/core/test_tracers.py b/tests/core/test_tracers.py index c538346a..66410692 100644 --- a/tests/core/test_tracers.py +++ b/tests/core/test_tracers.py @@ -403,7 +403,8 @@ def test_log_aggregate(tracer): lines = tracer.computation_log.lines(aggregate=True) assert ( - lines[0].strip() == "A<2017, (default)> = {'avg': np.float64(1.0), 'max': np.int64(1), 'min': np.int64(1)}" + lines[0].strip() + == "A<2017, (default)> = {'avg': np.float64(1.0), 'max': np.int64(1), 'min': np.int64(1)}" ) From 30d234b172344e55be71d0c30d70b6e065c2d0cf Mon Sep 17 00:00:00 2001 From: noman404 Date: Thu, 30 Jan 2025 02:48:21 +0800 Subject: [PATCH 11/21] added change log --- changelog_entry.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29b..ab83c9f9 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,6 @@ +- bump: major + changes: + changed: + - python 3.13.0 + - numpy 2.1.0 + date: 2025-01-30 02:47:17 \ No newline at end of file From 3de837c2e8d8922d6de582f73f3c910e0e2cd731 Mon Sep 17 00:00:00 2001 From: noman404 Date: Fri, 31 Jan 2025 07:40:50 +0800 Subject: [PATCH 12/21] updated change log, fix lint issue --- changelog_entry.yaml | 3 +-- tests/conftest.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index ab83c9f9..7ed71d79 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -2,5 +2,4 @@ changes: changed: - python 3.13.0 - - numpy 2.1.0 - date: 2025-01-30 02:47:17 \ No newline at end of file + - numpy 2.1.0 \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 1da05584..6ab38421 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,5 @@ """ -This module directs PyTest to include certain global fixtures when running tests. +This module directs PyTest to include certain global fixtures when running tests. """ pytest_plugins = [ From 0e73e54a60122ca774192c19825c5f2de117c98e Mon Sep 17 00:00:00 2001 From: noman404 Date: Fri, 31 Jan 2025 09:10:30 +0800 Subject: [PATCH 13/21] added new tests --- tests/core/commons/test_formulas.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/core/commons/test_formulas.py b/tests/core/commons/test_formulas.py index db22fc47..ecd3e182 100644 --- a/tests/core/commons/test_formulas.py +++ b/tests/core/commons/test_formulas.py @@ -79,3 +79,13 @@ def test_switch_when_values_are_empty(): with pytest.raises(AssertionError): assert commons.switch(conditions, value_by_condition) + + +def test_concat_tuple_inputs(): + with pytest.raises(TypeError, match="First argument must not be a tuple."): + commons.concat(("a", "b"), numpy.array(["c", "d"])) + + with pytest.raises( + TypeError, match="Second argument must not be a tuple." + ): + commons.concat(numpy.array(["a", "b"]), ("c", "d")) From 23250185c4bb28154d0ed55b49047245f6ad63bb Mon Sep 17 00:00:00 2001 From: noman404 Date: Fri, 31 Jan 2025 09:15:17 +0800 Subject: [PATCH 14/21] added GH workflow test for python 3.13 --- .github/workflows/pr.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 64cfaf51..007d52fc 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -32,13 +32,17 @@ jobs: run: .github/is-version-number-acceptable.sh Test: runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ "3.12", "3.13" ] + fail-fast: false steps: - name: Checkout repo uses: actions/checkout@v3 - - name: Set up Python + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: - python-version: "3.12" + python-version: ${{ matrix.python-version }} - name: Install package run: make install - name: Run tests From 03bbfb966efd897b0afc19811ccc9b403cf370da Mon Sep 17 00:00:00 2001 From: noman404 Date: Fri, 7 Feb 2025 10:06:28 +0800 Subject: [PATCH 15/21] point to updated PE-US, added windows in GH workflow --- .github/workflows/pr.yaml | 1 + Makefile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 94c57b15..3d21f0b1 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -34,6 +34,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, windows-latest ] + python-version: [ "3.12", "3.13" ] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/Makefile b/Makefile index 33da57ef..957e2adb 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ format: install: pip install -e ".[dev]" --config-settings editable_mode=compat - pip install policyengine-us + pip install git+https://github.com/noman404/policyengine-us.git@noman404/python3.13 pip install policyengine-uk test-country-template: From f24e1a62c63e7e410d99d04ce905e5faf1752971 Mon Sep 17 00:00:00 2001 From: noman404 Date: Fri, 7 Feb 2025 10:24:22 +0800 Subject: [PATCH 16/21] added long path ignore for windows --- .github/workflows/pr.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 3d21f0b1..94d08903 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -40,6 +40,12 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 + - name: Enable long paths in Git (Windows Only) + if: runner.os == 'Windows' + run: git config --system core.longpaths true + - name: Enable Win32 long paths via registry (Windows Only) + if: runner.os == 'Windows' + run: reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /v LongPathsEnabled /t REG_DWORD /d 1 /f - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: From 7e3533061a8dcc2ddadf2fc9d455051b1dd322b0 Mon Sep 17 00:00:00 2001 From: noman404 Date: Fri, 7 Feb 2025 22:17:48 +0800 Subject: [PATCH 17/21] added standard-imghdr --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index f9167c3f..311e9a56 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ "pyvis>=0.3.2", "microdf_python>=0.4.3", "huggingface_hub>=0.25.1", + "standard-imghdr" ] dev_requirements = [ From 83f47bbdd675e27d3f9761c4c40e6c680aef2a6c Mon Sep 17 00:00:00 2001 From: noman404 Date: Fri, 7 Feb 2025 22:19:35 +0800 Subject: [PATCH 18/21] imghdr fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 311e9a56..49a611ba 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ "pyvis>=0.3.2", "microdf_python>=0.4.3", "huggingface_hub>=0.25.1", - "standard-imghdr" + "standard-imghdr", ] dev_requirements = [ From 2044b5eb7ca66dbf72a63cb3649d7dac5002e8d9 Mon Sep 17 00:00:00 2001 From: noman404 Date: Fri, 21 Feb 2025 10:38:08 +0800 Subject: [PATCH 19/21] argsort fix --- policyengine_core/populations/population.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/policyengine_core/populations/population.py b/policyengine_core/populations/population.py index a04b8de6..92198b16 100644 --- a/policyengine_core/populations/population.py +++ b/policyengine_core/populations/population.py @@ -253,7 +253,11 @@ def get_rank( # We double-argsort all lines of the matrix. # Double-argsorting gets the rank of each value once sorted # For instance, if x = [3,1,6,4,0], y = numpy.argsort(x) is [4, 1, 0, 3, 2] (because the value with index 4 is the smallest one, the value with index 1 the second smallest, etc.) and z = numpy.argsort(y) is [2, 1, 4, 3, 0], the rank of each value. - sorted_matrix = numpy.argsort(numpy.argsort(matrix)) + + first_argsort = numpy.argsort(matrix, axis=1, kind="stable") + # because of the infinities the first sort creates positional indices + # The second argsort converts these positions to ranks + sorted_matrix = numpy.argsort(first_argsort, axis=1, kind="stable") # Build the result vector by taking for each person the value in the right line (corresponding to its household id) and the right column (corresponding to its position) result = sorted_matrix[ids, positions] From c012d8c99d4768944feb6744d5f129ce24d1a9ef Mon Sep 17 00:00:00 2001 From: noman404 Date: Sat, 22 Feb 2025 08:08:43 +0800 Subject: [PATCH 20/21] python build error fix for workflow --- .github/workflows/pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 94d08903..11b0f47d 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -34,7 +34,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, windows-latest ] - python-version: [ "3.12", "3.13" ] + python-version: [ "3.12", "3.13.2" ] fail-fast: false runs-on: ${{ matrix.os }} steps: From 779ef0a64a29ffd8ffe74fbada47a39ffb59e1cf Mon Sep 17 00:00:00 2001 From: noman404 Date: Tue, 25 Feb 2025 01:00:25 +0800 Subject: [PATCH 21/21] added code comment for sorting --- .github/workflows/pr.yaml | 2 +- policyengine_core/populations/population.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 11b0f47d..94d08903 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -34,7 +34,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, windows-latest ] - python-version: [ "3.12", "3.13.2" ] + python-version: [ "3.12", "3.13" ] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/policyengine_core/populations/population.py b/policyengine_core/populations/population.py index 92198b16..a6d992cb 100644 --- a/policyengine_core/populations/population.py +++ b/policyengine_core/populations/population.py @@ -254,9 +254,9 @@ def get_rank( # Double-argsorting gets the rank of each value once sorted # For instance, if x = [3,1,6,4,0], y = numpy.argsort(x) is [4, 1, 0, 3, 2] (because the value with index 4 is the smallest one, the value with index 1 the second smallest, etc.) and z = numpy.argsort(y) is [2, 1, 4, 3, 0], the rank of each value. - first_argsort = numpy.argsort(matrix, axis=1, kind="stable") # because of the infinities the first sort creates positional indices - # The second argsort converts these positions to ranks + # The second argsort converts these positions to ranks, thus fixes the broken sort issue + first_argsort = numpy.argsort(matrix, axis=1, kind="stable") sorted_matrix = numpy.argsort(first_argsort, axis=1, kind="stable") # Build the result vector by taking for each person the value in the right line (corresponding to its household id) and the right column (corresponding to its position)