From ce4e8fe993bfa90aad1f2a52e66e238760e66b2c Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Thu, 29 Feb 2024 12:41:20 -0500 Subject: [PATCH] chore: Rename private variables and methods to use timefold prefix; update docs --- .github/workflows/pull_request.yml | 24 +- .github/workflows/sonarcloud.yml | 47 ++- LICENSE | 201 ----------- README.md | 49 ++- jpyinterpreter/examples/example.py | 2 +- jpyinterpreter/examples/pom.xml | 12 +- .../jpyinterpreter/types/BuiltinTypes.java | 2 +- pom.xml | 4 - pyproject.toml | 2 +- setup.py | 5 - tests/test_solver_manager.py | 4 +- .../solver/python/OptaPyException.java | 13 - .../ai/timefold/solver/python/PythonList.java | 14 +- .../timefold/solver/python/PythonObject.java | 4 +- .../python/PythonPlanningSolutionCloner.java | 8 +- .../timefold/solver/python/PythonSolver.java | 12 +- .../solver/python/PythonWrapperGenerator.java | 80 ++--- .../solver/python/TimefoldException.java | 13 + ...ence.java => TimefoldObjectReference.java} | 8 +- .../src/main/python/__init__.py | 4 +- .../src/main/python/annotations.py | 122 +++---- .../src/main/python/jars/__init__.py | 0 .../src/main/python/jpype_type_conversions.py | 2 +- .../src/main/python/timefold_api_wrappers.py | 14 +- .../src/main/python/timefold_java_interop.py | 336 +++++++++--------- .../src/main/python/timefold_python_logger.py | 8 +- .../src/main/python/types/__init__.py | 2 +- timefold/__init__.py | 1 - timefold/timefold.py | 9 - 29 files changed, 381 insertions(+), 621 deletions(-) delete mode 100644 LICENSE delete mode 100644 timefold-solver-python-core/src/main/java/ai/timefold/solver/python/OptaPyException.java create mode 100644 timefold-solver-python-core/src/main/java/ai/timefold/solver/python/TimefoldException.java rename timefold-solver-python-core/src/main/java/ai/timefold/solver/python/{OptaPyObjectReference.java => TimefoldObjectReference.java} (82%) delete mode 100644 timefold-solver-python-core/src/main/python/jars/__init__.py delete mode 100644 timefold/__init__.py delete mode 100644 timefold/timefold.py diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 72696e7b..69c7101d 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -23,24 +23,20 @@ jobs: strategy: matrix: os: [ ubuntu-latest ] - java-version: [ 11, 17 ] - maven-version: [ '3.8.6' ] + java-version: [ 17 ] fail-fast: false runs-on: ${{ matrix.os }} steps: - name: Check out repository code - uses: actions/checkout@v3 - with: - fetch-depth: 0 + uses: actions/checkout@v4 - - name: Java and Maven Setup - uses: kiegroup/kogito-pipelines/.ci/actions/maven@main + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} - maven-version: ${{ matrix.maven-version }} - cache-key-prefix: ${{ runner.os }}-${{ matrix.java-version }}-maven${{ matrix.maven-version }} - + distribution: 'temurin' + cache: 'maven' # Need to install both Python 3.9, Python 3.10 and Python 3.11 for tox (has to be in the same run) # Feature Request for setup action: https://github.com/actions/setup-python/issues/98 - name: Python 3.9 Setup @@ -50,7 +46,6 @@ jobs: cache: 'pip' cache-dependency-path: | **/setup.py - - name: Python 3.10 Setup uses: actions/setup-python@v4 with: @@ -58,7 +53,6 @@ jobs: cache: 'pip' cache-dependency-path: | **/setup.py - - name: Python 3.11 Setup uses: actions/setup-python@v4 with: @@ -66,18 +60,14 @@ jobs: cache: 'pip' cache-dependency-path: | **/setup.py - - name: Install tox run: python -m pip install --upgrade pip pip install tox pytest - - name: Build with Maven to install parent poms for python build run: mvn -B --fail-at-end clean install - - - name: Run tox on optapy test suite + - name: Run tox on timefold solver python test suite run: python -m tox - - name: Run tox on jpyinterpreter test suite working-directory: ./jpyinterpreter run: python -m tox diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index f5cf6e4a..c1ad8412 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,4 +1,4 @@ -# Runs the SonarCloud analysis of the optapy main branch after a PR is merged. +# Runs the SonarCloud analysis of the Timefold Solver Python main branch after a PR is merged. name: SonarCloud Analysis on: @@ -23,23 +23,31 @@ jobs: matrix: os: [ ubuntu-latest ] java-version: [ 17 ] # JaCoCo segfaults Java 11 JVM but not Java 17 JVM when Python tests finish - maven-version: [ '3.8.6' ] fail-fast: false runs-on: ${{ matrix.os }} steps: - - name: Check out repository code - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - fetch-depth: 0 - - - name: Java and Maven Setup - uses: kiegroup/kogito-pipelines/.ci/actions/maven@main + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + ref: ${{ github.event.pull_request.head.sha }} # The GHA event will pull the main branch by default, and we must specify the PR reference version + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} - maven-version: ${{ matrix.maven-version }} - cache-key-prefix: ${{ runner.os }}-${{ matrix.java-version }}-maven${{ matrix.maven-version }} - + distribution: 'temurin' + - name: Cache SonarCloud packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v4 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 # Need to install both Python 3.9, Python 3.10 and Python 3.11 for tox (has to be in the same run) # Feature Request for setup action: https://github.com/actions/setup-python/issues/98 - name: Python 3.9 Setup @@ -49,7 +57,6 @@ jobs: cache: 'pip' cache-dependency-path: | **/setup.py - - name: Python 3.10 Setup uses: actions/setup-python@v4 with: @@ -57,7 +64,6 @@ jobs: cache: 'pip' cache-dependency-path: | **/setup.py - - name: Python 3.11 Setup uses: actions/setup-python@v4 with: @@ -65,27 +71,21 @@ jobs: cache: 'pip' cache-dependency-path: | **/setup.py - - name: Install tox run: python -m pip install --upgrade pip pip install tox coverage pytest pytest-cov - - name: Build with Maven to measure code coverage run: mvn -B --fail-at-end clean install -Prun-code-coverage - - name: Get JaCoCo Agent - run: mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:get -Dartifact=org.jacoco:org.jacoco.agent:0.8.8:jar:runtime -Ddest=target/jacocoagent.jar - - - name: Run tox to measure optapy code coverage from Python tests + run: mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:get -Dartifact=org.jacoco:org.jacoco.agent:0.8.11:jar:runtime -Ddest=target/jacocoagent.jar + - name: Run tox to measure timefold solver python code coverage from Python tests continue-on-error: true # Sometimes the JVM segfaults on SUCCESSFUL tests with Java 17 (and always with Java 11) - run: python -m tox -- --cov=optapy --cov-report=xml:target/coverage.xml --cov-config=tox.ini --cov-branch --cov-append --jacoco-agent=./target/jacocoagent.jar - + run: python -m tox -- --cov=timefold-solver --cov-report=xml:target/coverage.xml --cov-config=tox.ini --cov-branch --cov-append --jacoco-agent=./target/jacocoagent.jar - name: Run tox to measure jpyinterpreter code coverage from Python tests continue-on-error: true # Sometimes the JVM segfaults on SUCCESSFUL tests with Java 17 (and always with Java 11) working-directory: ./jpyinterpreter run: python -m tox -- --cov=jpyinterpreter --cov-report=xml:target/coverage.xml --cov-config=tox.ini --cov-branch --cov-append --jacoco-agent=../target/jacocoagent.jar --jacoco-output=../target/jacoco.exec - # Because we are using JPype, and JPype add it own import hook, we need to use --import-mode=importlib in pytest # This seems to create an issue in test coverage, where it reports coverage inside the tox virtual environment, # instead of coverage inside sources. For instance, for a package, it will report: @@ -99,9 +99,8 @@ jobs: # separate coverage files into one. - name: Fix Python test coverage paths run: python fix-coverage-paths.py - - name: Run SonarCloud analysis env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONARCLOUD_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }} - run: mvn -B --fail-at-end validate -Psonarcloud-analysis -Dsonar.projectKey=optapy_optapy -Dsonar.login=${{ env.SONARCLOUD_TOKEN }} \ No newline at end of file + run: mvn -B --fail-at-end validate -Psonarcloud-analysis -Dsonar.projectKey=timefold_solver_python -Dsonar.login=${{ env.SONARCLOUD_TOKEN }} \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index 332542ae..b3fd3237 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,28 @@ -# OptaPy +# Timefold Solver -[![PyPI](https://img.shields.io/pypi/v/optapy "PyPI")](https://pypi.org/project/optapy/) -[![Binder](https://mybinder.org/badge_logo.svg "Launch on Binder")](https://mybinder.org/v2/gh/optapy/optapy-quickstarts/stable?filepath=school-timetabling/school-timetabling-quickstart.ipynb) +[![PyPI](https://img.shields.io/pypi/v/timefold "PyPI")](https://pypi.org/project/timefold-solver/) -[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=optapy_optapy&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=optapy_optapy) -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=optapy_optapy&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=optapy_optapy) -[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=optapy_optapy&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=optapy_optapy) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=optapy_optapy&metric=coverage)](https://sonarcloud.io/summary/new_code?id=optapy_optapy) +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=timefold_solver_python&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=timefold_solver_python) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=timefold_solver_python&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=timefold_solver_python) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=timefold_solver_python&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=timefold_solver_python) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=timefold_solver_python&metric=coverage)](https://sonarcloud.io/summary/new_code?id=timefold_solver_python) -OptaPy is *an AI constraint solver for Python* to optimize +Timefold Solver is *an AI constraint solver for Python* to optimize the Vehicle Routing Problem, Employee Rostering, Maintenance Scheduling, Task Assignment, School Timetabling, Cloud Optimization, Conference Scheduling, Job Shop Scheduling, Bin Packing and many more planning problems. -OptaPy wraps the [OptaPlanner](https://www.optaplanner.org/) engine internally, -but using OptaPy in Python is significantly slower than using OptaPlanner in Java or Kotlin. - -[Try the OptaPy Jupyter notebook.](https://mybinder.org/v2/gh/optapy/optapy-quickstarts/stable?filepath=school-timetabling/school-timetabling-quickstart.ipynb) - +Using Timefold in Python is significantly slower than using Timefold in Java or Kotlin. ## Requirements - [Install Python 3.9 or later.](https://www.python.org) -- [Install JDK 11 or later](https://adoptium.net) with the environment variable `JAVA_HOME` configured to the JDK installation directory. +- [Install JDK 17 or later](https://adoptium.net) with the environment variable `JAVA_HOME` configured to the JDK installation directory. ## Source code overview ### Domain -In OptaPy, the domain has three parts: +In Timefold, the domain has three parts: - Problem Facts, which do not change - Planning Entities, which have one or more planning variables @@ -38,7 +33,7 @@ In OptaPy, the domain has three parts: To declare Problem Facts, use the `@problem_fact` decorator ```python -from optapy import problem_fact +from timefold.solver import problem_fact @problem_fact @@ -55,7 +50,7 @@ class Timeslot: To declare Planning Entities, use the `@planning_entity` decorator ```python -from optapy import planning_entity, planning_id, planning_variable +from timefold.solver import planning_entity, planning_id, planning_variable @planning_entity class Lesson: @@ -86,7 +81,7 @@ class Lesson: self.room = new_room ``` -- `@planning_variable` method decorators are used to indicate what fields can change. MUST begin with get and have a corresponding set method (i.e. `get_room(self)`, `set_room(self, newRoom)`). The first parameter of the decorator is the type of the Planning Variable (required). The `value_range_provider_refs` parameter tells OptaPlanner what value range providers on the Planning Solution this Planning Variable can take values from. +- `@planning_variable` method decorators are used to indicate what fields can change. MUST begin with get and have a corresponding set method (i.e. `get_room(self)`, `set_room(self, newRoom)`). The first parameter of the decorator is the type of the Planning Variable (required). The `value_range_provider_refs` parameter tells Timefold what value range providers on the Planning Solution this Planning Variable can take values from. - `@planning_id` is used to uniquely identify an entity object of a particular class. The same Planning Id can be used on entities of different classes, but the ids of all entities in the same class must be different. @@ -95,7 +90,7 @@ class Lesson: To declare the Planning Solution, use the `@planning_solution` decorator ```python -from optapy import planning_solution, problem_fact_collection_property, value_range_provider, planning_entity_collection_property, planning_score +from timefold.solver import planning_solution, problem_fact_collection_property, value_range_provider, planning_entity_collection_property, planning_score @planning_solution class TimeTable: @@ -133,15 +128,15 @@ class TimeTable: - `@planning_entity_collection_property(type)` is used to indicate a method returns Planning Entities. The first parameter of the decorator is the type of the Planning Entity Collection (required). It should be a list. -- `@planning_score(scoreType)` is used to tell OptaPlanner what field holds the score. The method MUST begin with get and have a corresponding set method (i.e. `get_score(self)`, `set_score(self, score)`). The first parameter of the decorator is the score type (required). +- `@planning_score(scoreType)` is used to tell Timefold what field holds the score. The method MUST begin with get and have a corresponding set method (i.e. `get_score(self)`, `set_score(self, score)`). The first parameter of the decorator is the score type (required). ### Constraints You define your constraints by using the ConstraintFactory ```python from domain import Lesson -from optapy import constraint_provider -from optapy.types import Joiners, HardSoftScore +from timefold.solver import constraint_provider +from timefold.solver.types import Joiners, HardSoftScore @constraint_provider @@ -161,13 +156,14 @@ def room_conflict(constraint_factory): Joiners.equal(lambda lesson: lesson.room)) \ .penalize("Room conflict", HardSoftScore.ONE_HARD) ``` -for more details on Constraint Streams, see https://www.optaplanner.org/docs/optaplanner/latest/constraint-streams/constraint-streams.html +for more details on Constraint Streams, +see https://timefold.ai/docs/timefold-solver/latest/constraints-and-score/score-calculation. ### Solve ```python -from optapy import solver_factory_create -from optapy.types import SolverConfig, Duration +from timefold.solver import solver_factory_create +from timefold.solver.types import SolverConfig, Duration from constraints import define_constraints from domain import TimeTable, Lesson, generate_problem @@ -185,5 +181,4 @@ variables set to the final best solution found. ## More information -For quickstarts, visit the [optapy quickstart repository](https://github.com/optapy/optapy-quickstarts). -For a full API spec, visit [the OptaPy Documentation](https://www.optapy.org). +For a full API spec, visit [the Timefold Documentation](https://timefold.ai/docs/timefold-solver/latest). diff --git a/jpyinterpreter/examples/example.py b/jpyinterpreter/examples/example.py index 4350ce53..111a8596 100644 --- a/jpyinterpreter/examples/example.py +++ b/jpyinterpreter/examples/example.py @@ -1,7 +1,7 @@ import jpype.imports import jpyinterpreter -jpyinterpreter.init(path=['target/example-8.19.0.Final.jar']) +jpyinterpreter.init(path=['target/example-1.0.0.jar']) from java.util.function import Function diff --git a/jpyinterpreter/examples/pom.xml b/jpyinterpreter/examples/pom.xml index 8a9c2243..6a7b3f60 100644 --- a/jpyinterpreter/examples/pom.xml +++ b/jpyinterpreter/examples/pom.xml @@ -4,15 +4,15 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.optaplanner - optaplanner-build-parent - 8.19.0.Final - + ai.timefold.solver + timefold-solver-build-parent + 1.7.0 org.acme example Example + 1.0.0 acme @@ -42,10 +42,6 @@ org.apache.maven.plugins maven-compiler-plugin - - 8 - 8 - diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/BuiltinTypes.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/BuiltinTypes.java index 9bf1ce38..63e845cf 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/BuiltinTypes.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/BuiltinTypes.java @@ -93,7 +93,7 @@ public class BuiltinTypes { public static ClassLoader asmClassLoader = new ClassLoader() { // getName() is an abstract method in Java 11 but not in Java 8 public String getName() { - return "OptaPlanner Gizmo Python Bytecode ClassLoader"; + return "JPyInterpreter Python Bytecode ClassLoader"; } @Override diff --git a/pom.xml b/pom.xml index 3e612acf..026e75b1 100644 --- a/pom.xml +++ b/pom.xml @@ -12,11 +12,7 @@ pom - 11 - 11 - 11 UTF-8 - 1.7.0a0 4.4 ${project.artifactId} diff --git a/pyproject.toml b/pyproject.toml index 3228d3a0..66a5388f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ requires = [ "setuptools>=69.1.1", "stubgenj>=0.2.5", - "JPype1>=1.4.1", + "JPype1>=1.5.0", "wheel" ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index becf6690..d3dbeb21 100644 --- a/setup.py +++ b/setup.py @@ -60,10 +60,6 @@ def run(self): for file in classpath_jars: copyfile(file, os.path.join(self.build_lib, 'timefold', 'solver', 'jars', os.path.basename(file))) - # Make timefold a Python module - fp = open(os.path.join(self.build_lib, 'timefold', '__init__.py'), 'w') - fp.close() - # Make timefold.solver.jars a Python module fp = open(os.path.join(self.build_lib, 'timefold', 'solver', 'jars', '__init__.py'), 'w') fp.close() @@ -103,7 +99,6 @@ def find_stub_files(stub_root: str): ], packages=['timefold.solver', 'timefold.solver.config', 'timefold.solver.constraint', 'timefold.solver.score', 'timefold.solver.types', 'timefold.solver.test', 'jpyinterpreter', - 'timefold.solver.jars', 'java-stubs', 'jpype-stubs', 'ai-stubs'], package_dir={ 'timefold.solver': 'timefold-solver-python-core/src/main/python', diff --git a/tests/test_solver_manager.py b/tests/test_solver_manager.py index 381e5fd9..d6145a0a 100644 --- a/tests/test_solver_manager.py +++ b/tests/test_solver_manager.py @@ -137,7 +137,7 @@ def assert_solver_run(solver_manager, solver_job): assert 3 in value_list assert solver_manager.getSolverStatus(1) == SolverStatus.NOT_SOLVING time.sleep(0.1) # Sleep so cleanup is guaranteed to be executed - solver_run_dicts = solver_manager._optapy_debug_get_solver_runs_dicts() + solver_run_dicts = solver_manager._timefold_debug_get_solver_runs_dicts() assert len(solver_run_dicts['solver_run_id_to_refs']) == 0 def assert_problem_change_solver_run(solver_manager, solver_job): @@ -153,7 +153,7 @@ def assert_problem_change_solver_run(solver_manager, solver_job): assert solution.value_range[0].value == 6 assert solver_manager.getSolverStatus(1) == SolverStatus.NOT_SOLVING time.sleep(0.1) # Sleep so cleanup is guaranteed to be executed - solver_run_dicts = solver_manager._optapy_debug_get_solver_runs_dicts() + solver_run_dicts = solver_manager._timefold_debug_get_solver_runs_dicts() assert len(solver_run_dicts['solver_run_id_to_refs']) == 0 with timefold.solver.solver_manager_create(solver_config) as solver_manager: diff --git a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/OptaPyException.java b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/OptaPyException.java deleted file mode 100644 index a14e75ab..00000000 --- a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/OptaPyException.java +++ /dev/null @@ -1,13 +0,0 @@ -package ai.timefold.solver.python; - -public class OptaPyException extends RuntimeException { - - public OptaPyException(String message) { - super(message); - } - - public OptaPyException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonList.java b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonList.java index 76213cd8..0d1c3d54 100644 --- a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonList.java +++ b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonList.java @@ -111,12 +111,12 @@ public PythonList(OpaquePythonReference pythonListOpaqueReference, Number id, Ma } @Override - public OpaquePythonReference get__optapy_Id() { + public OpaquePythonReference get__timefold_id() { return pythonListOpaqueReference; } @Override - public Map get__optapy_reference_map() { + public Map get__timefold_reference_map() { return idMap; } @@ -127,7 +127,7 @@ public void forceUpdate() { if (o instanceof OpaquePythonReference) { addItemToPythonList.apply(pythonListOpaqueReference, o); } else if (o instanceof PythonObject) { - addItemToPythonList.apply(pythonListOpaqueReference, ((PythonObject) o).get__optapy_Id()); + addItemToPythonList.apply(pythonListOpaqueReference, ((PythonObject) o).get__timefold_id()); } else { addItemToPythonList.apply(pythonListOpaqueReference, o); } @@ -216,7 +216,7 @@ public boolean add(Object t) { if (t instanceof OpaquePythonReference) { addItemToPythonList.apply(pythonListOpaqueReference, t); } else if (t instanceof PythonObject) { - addItemToPythonList.apply(pythonListOpaqueReference, ((PythonObject) t).get__optapy_Id()); + addItemToPythonList.apply(pythonListOpaqueReference, ((PythonObject) t).get__timefold_id()); } else { addItemToPythonList.apply(pythonListOpaqueReference, t); } @@ -233,7 +233,7 @@ public boolean remove(Object t) { if (t instanceof OpaquePythonReference) { return removeItemFromPythonList.apply(pythonListOpaqueReference, t); } else if (t instanceof PythonObject) { - return removeItemFromPythonList.apply(pythonListOpaqueReference, ((PythonObject) t).get__optapy_Id()); + return removeItemFromPythonList.apply(pythonListOpaqueReference, ((PythonObject) t).get__timefold_id()); } else { return removeItemFromPythonList.apply(pythonListOpaqueReference, t); } @@ -348,7 +348,7 @@ public Object set(int i, Object t) { if (t instanceof OpaquePythonReference) { setItemAtIndexInPythonList.apply(pythonListOpaqueReference, i, t); } else if (t instanceof PythonObject) { - setItemAtIndexInPythonList.apply(pythonListOpaqueReference, i, ((PythonObject) t).get__optapy_Id()); + setItemAtIndexInPythonList.apply(pythonListOpaqueReference, i, ((PythonObject) t).get__timefold_id()); } else { setItemAtIndexInPythonList.apply(pythonListOpaqueReference, i, t); } @@ -365,7 +365,7 @@ public void add(int i, Object t) { if (t instanceof OpaquePythonReference) { addItemAtIndexInPythonList.apply(pythonListOpaqueReference, i, t); } else if (t instanceof PythonObject) { - addItemAtIndexInPythonList.apply(pythonListOpaqueReference, i, ((PythonObject) t).get__optapy_Id()); + addItemAtIndexInPythonList.apply(pythonListOpaqueReference, i, ((PythonObject) t).get__timefold_id()); } else { addItemAtIndexInPythonList.apply(pythonListOpaqueReference, i, t); } diff --git a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonObject.java b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonObject.java index 8dae8c26..bda202bc 100644 --- a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonObject.java +++ b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonObject.java @@ -20,7 +20,7 @@ public interface PythonObject extends PythonLikeObject { * @return An opaque pointer to the Python Object represented by * this PythonObject. */ - OpaquePythonReference get__optapy_Id(); + OpaquePythonReference get__timefold_id(); /** * The Map of references that the planning solution that contains this @@ -28,7 +28,7 @@ public interface PythonObject extends PythonLikeObject { * * @return The map used to store references. */ - Map get__optapy_reference_map(); + Map get__timefold_reference_map(); void forceUpdate(); diff --git a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonPlanningSolutionCloner.java b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonPlanningSolutionCloner.java index 315fe242..bd79b09e 100644 --- a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonPlanningSolutionCloner.java +++ b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonPlanningSolutionCloner.java @@ -41,7 +41,7 @@ public Object cloneSolution(Object o) { // Wrap the deep cloned OpaquePythonReference into a new PythonObject PythonObject out = - (PythonObject) PythonWrapperGenerator.wrap(o.getClass(), planningClone, toClone.get__optapy_reference_map(), + (PythonObject) PythonWrapperGenerator.wrap(o.getClass(), planningClone, toClone.get__timefold_reference_map(), pythonSetter); Map oldIdMap = new HashMap<>(); @@ -51,13 +51,13 @@ public Object cloneSolution(Object o) { for (Number id : oldIdMap.keySet()) { if (!newIdMap.containsKey(id)) { - toClone.get__optapy_reference_map().remove(id); + toClone.get__timefold_reference_map().remove(id); } else { newIdMap.remove(id); } } - toClone.get__optapy_reference_map().putAll(newIdMap); + toClone.get__timefold_reference_map().putAll(newIdMap); // Mirror the reference map (not pass a reference to it) // so Score + list variables can be safely garbage collected in Python @@ -65,7 +65,7 @@ public Object cloneSolution(Object o) { // which is used when cloning. If score/list variable was garbage collected by Python, another // Python Object can have the same id, leading to the old value in the map being returned, // causing an exception (or worse, a subtle bug)) - Map newReferenceMap = new MirrorWithExtrasMap<>(out.get__optapy_reference_map()); + Map newReferenceMap = new MirrorWithExtrasMap<>(out.get__timefold_reference_map()); out.readFromPythonObject(Collections.newSetFromMap(new IdentityHashMap<>()), newReferenceMap); diff --git a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonSolver.java b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonSolver.java index de9ea7ee..a1c5a773 100644 --- a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonSolver.java +++ b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonSolver.java @@ -30,7 +30,7 @@ public static Object wrapProblem(Class solutionClass, OpaquePythonReference p getNewReferenceMap(), onlyUseJavaSettersForThisInstance ? PythonWrapperGenerator.NONE_PYTHON_SETTER : PythonWrapperGenerator.pythonObjectIdAndAttributeSetter); - out.visitIds(out.get__optapy_reference_map()); + out.visitIds(out.get__timefold_reference_map()); // Mirror the reference map (not pass a reference to it) // so Score + list variables can be safely garbage collected in Python @@ -39,10 +39,10 @@ public static Object wrapProblem(Class solutionClass, OpaquePythonReference p // Python Object can have the same id, leading to the old value in the map being returned, // causing an exception (or worse, a subtle bug)) out.readFromPythonObject(Collections.newSetFromMap(new IdentityHashMap<>()), - new MirrorWithExtrasMap<>(out.get__optapy_reference_map())); + new MirrorWithExtrasMap<>(out.get__timefold_reference_map())); return out; } catch (Throwable t) { - throw new OptaPyException("A problem occurred when wrapping the python problem (" + + throw new TimefoldException("A problem occurred when wrapping the python problem (" + PythonWrapperGenerator.getPythonObjectString(problem) + "). Maybe an annotation was passed an incorrect type " + "(for example, @problem_fact_collection_property(str) " + @@ -56,7 +56,7 @@ public static Object wrapFact(Class factClass, OpaquePythonReference fact, Ma PythonObject out = (PythonObject) PythonWrapperGenerator.wrap(factClass, fact, referenceMap, PythonWrapperGenerator.NONE_PYTHON_SETTER); - out.visitIds(out.get__optapy_reference_map()); + out.visitIds(out.get__timefold_reference_map()); // Mirror the reference map (not pass a reference to it) // so Score + list variables can be safely garbage collected in Python @@ -65,10 +65,10 @@ public static Object wrapFact(Class factClass, OpaquePythonReference fact, Ma // Python Object can have the same id, leading to the old value in the map being returned, // causing an exception (or worse, a subtle bug)) out.readFromPythonObject(Collections.newSetFromMap(new IdentityHashMap<>()), - new MirrorWithExtrasMap<>(out.get__optapy_reference_map())); + new MirrorWithExtrasMap<>(out.get__timefold_reference_map())); return out; } catch (Throwable t) { - throw new OptaPyException("A problem occurred when wrapping the python fact (" + + throw new TimefoldException("A problem occurred when wrapping the python fact (" + PythonWrapperGenerator.getPythonObjectString(fact) + ").\n" + "Maybe an annotation was passed an incorrect type " + "(for example, @problem_fact_collection_property(str) " + diff --git a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonWrapperGenerator.java b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonWrapperGenerator.java index e623ea8d..a6c28cf6 100644 --- a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonWrapperGenerator.java +++ b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/PythonWrapperGenerator.java @@ -163,7 +163,7 @@ public static String getPythonObjectString(OpaquePythonReference pythonObject) { @SuppressWarnings("unused") public static OpaquePythonReference getPythonObject(PythonObject pythonObject) { - return pythonObject.get__optapy_Id(); + return pythonObject.get__timefold_id(); } public static OpaquePythonReference getPythonObject(PythonComparable pythonObject) { @@ -175,7 +175,7 @@ public static ClassLoader getClassLoaderForAliasMap(Map> aliasM return new ClassLoader() { // getName() is an abstract method in Java 11 but not in Java 8 public String getName() { - return "OptaPy Alias Map ClassLoader"; + return "Timefold Alias Map ClassLoader"; } @Override @@ -192,7 +192,7 @@ public Class findClass(String name) throws ClassNotFoundException { } public static Number getPythonObjectId(PythonObject pythonObject) { - return pythonObjectToId.apply(pythonObject.get__optapy_Id()); + return pythonObjectToId.apply(pythonObject.get__timefold_id()); } @SuppressWarnings("unused") // used by variable listener/custom shadow variable on Python side @@ -200,12 +200,12 @@ public static void updateVariableFromPythonObject(PythonObject object, String va throws IllegalAccessException, InvocationTargetException { Object newValue; try { - newValue = getValueFromPythonObject(object.get__optapy_Id(), "get_" + variableName); - } catch (OptaPyException e1) { + newValue = getValueFromPythonObject(object.get__timefold_id(), "get_" + variableName); + } catch (TimefoldException e1) { try { - newValue = getValueFromPythonObject(object.get__optapy_Id(), + newValue = getValueFromPythonObject(object.get__timefold_id(), "get" + Character.toUpperCase(variableName.charAt(0)) + variableName.substring(1)); - } catch (OptaPyException e2) { + } catch (TimefoldException e2) { throw new IllegalArgumentException( "Unable to find variable (" + variableName + ") on entity (" + object + ")."); } @@ -277,12 +277,12 @@ public static String getCollectionSignature(Class collectionClass, Class< } // Holds the OpaquePythonReference - static final String PYTHON_BINDING_FIELD_NAME = "__optaplannerPythonValue"; - static final String REFERENCE_MAP_FIELD_NAME = "__optaplannerReferenceMap"; + static final String PYTHON_BINDING_FIELD_NAME = "__timefoldPythonValue"; + static final String REFERENCE_MAP_FIELD_NAME = "__timefoldReferenceMap"; - static final String PYTHON_SETTER_FIELD_NAME = "_optaplannerPythonSetter"; + static final String PYTHON_SETTER_FIELD_NAME = "_timefoldPythonSetter"; - static final String PYTHON_LIKE_VALUE_MAP_FIELD_NAME = "__optaplannerPythonLikeValueCacheMap"; + static final String PYTHON_LIKE_VALUE_MAP_FIELD_NAME = "__timefoldPythonLikeValueCacheMap"; static final String PYTHON_LIKE_TYPE_FIELD_NAME = "$TYPE"; static final TriFunction NONE_PYTHON_SETTER = (a, b, c) -> null; @@ -669,21 +669,21 @@ public static Class defineVariableListenerClass(String className, * * (none or @PlanningEntity or @PlanningSolution) * public class PojoForPythonObject implements PythonObject { - * OpaquePythonReference __optaplannerPythonValue; + * OpaquePythonReference __timefoldPythonValue; * String string$field; * AnotherPojoForPythonObject otherObject$field; * * public PojoForPythonObject(OpaquePythonReference reference, Number id, Map * pythonIdToPythonObjectMap) { - * this.__optaplannerPythonValue = reference; + * this.__timefoldPythonValue = reference; * pythonIdToPythonObjectMap.put(id, this); * string$field = PythonWrapperGenerator.getValueFromPythonObject(reference, "string"); * OpaquePythonReference otherObjectReference = PythonWrapperGenerator.getValueFromPythonObject(reference, "otherObject"); * otherObject$field = PythonWrapperGenerator.wrap(otherObjectReference, id, pythonIdToPythonObjectMap); * } * - * public OpaquePythonReference get__optapy_Id() { - * return __optaplannerPythonValue; + * public OpaquePythonReference get__timefold_id() { + * return __timefoldPythonValue; * } * * public String getStringField() { @@ -691,7 +691,7 @@ public static Class defineVariableListenerClass(String className, * } * * public void setStringField(String val) { - * PythonWrapperGenerator.setValueOnPythonObject(__optaplannerPythonValue, "string", val); + * PythonWrapperGenerator.setValueOnPythonObject(__timefoldPythonValue, "string", val); * this.string$field = val; * } * // Repeat for otherObject @@ -700,7 +700,7 @@ public static Class defineVariableListenerClass(String className, @SuppressWarnings("unused") public static Class definePlanningEntityClass(String className, Class parentClass, boolean defineEqualsAndHashcode, - List> optaplannerMethodAnnotations, + List> timefoldMethodAnnotations, Map planningEntityAnnotations) { if (classNameToBytecode.containsKey(className)) { try { @@ -743,7 +743,7 @@ public static Class definePlanningEntityClass(String className, Class pare pythonLikeValueMapField, pythonSetterField, pythonLikeTypeField, - optaplannerMethodAnnotations); + timefoldMethodAnnotations); } writeClassOutput(classNameToBytecode, className, classBytecodeHolder.get()); return createAndInitializeClass(className); @@ -752,7 +752,7 @@ public static Class definePlanningEntityClass(String className, Class pare @SuppressWarnings("unused") public static Class defineProblemFactClass(String className, Class parentClass, boolean defineEqualsAndHashcode, - List> optaplannerMethodAnnotations) { + List> timefoldMethodAnnotations) { if (classNameToBytecode.containsKey(className)) { try { return asmClassLoader.loadClass(className); @@ -784,7 +784,7 @@ public static Class defineProblemFactClass(String className, Class parentC generateWrapperMethods(classCreator, parentClass, GeneratedClassType.PROBLEM_FACT, defineEqualsAndHashcode, valueField, referenceMapField, pythonLikeValueMapField, pythonSetterField, pythonLikeTypeField, - optaplannerMethodAnnotations); + timefoldMethodAnnotations); } writeClassOutput(classNameToBytecode, className, classBytecodeHolder.get()); return createAndInitializeClass(className); @@ -793,7 +793,7 @@ public static Class defineProblemFactClass(String className, Class parentC @SuppressWarnings("unused") public static Class definePlanningSolutionClass(String className, Class parentClass, boolean defineEqualsAndHashcode, - List> optaplannerMethodAnnotations) { + List> timefoldMethodAnnotations) { if (classNameToBytecode.containsKey(className)) { try { return asmClassLoader.loadClass(className); @@ -826,7 +826,7 @@ public static Class definePlanningSolutionClass(String className, Class pa generateWrapperMethods(classCreator, parentClass, GeneratedClassType.PLANNING_SOLUTION, defineEqualsAndHashcode, valueField, referenceMapField, pythonLikeValueMapField, pythonSetterField, pythonLikeTypeField, - optaplannerMethodAnnotations); + timefoldMethodAnnotations); } writeClassOutput(classNameToBytecode, className, classBytecodeHolder.get()); return createAndInitializeClass(className); @@ -843,11 +843,11 @@ private static void print(BytecodeCreator methodCreator, ResultHandle toPrint) { private static void generateAsPointer(ClassCreator classCreator, FieldDescriptor valueField, FieldDescriptor referenceMapField, FieldDescriptor pythonLikeObjectValueField, FieldDescriptor typeField) { - MethodCreator methodCreator = classCreator.getMethodCreator("get__optapy_Id", OpaquePythonReference.class); + MethodCreator methodCreator = classCreator.getMethodCreator("get__timefold_id", OpaquePythonReference.class); ResultHandle valueResultHandle = methodCreator.readInstanceField(valueField, methodCreator.getThis()); methodCreator.returnValue(valueResultHandle); - methodCreator = classCreator.getMethodCreator("get__optapy_reference_map", Map.class); + methodCreator = classCreator.getMethodCreator("get__timefold_reference_map", Map.class); ResultHandle referenceMapResultHandle = methodCreator.readInstanceField(referenceMapField, methodCreator.getThis()); methodCreator.returnValue(referenceMapResultHandle); } @@ -907,7 +907,7 @@ private static void generateForceUpdate(ClassCreator classCreator, GeneratedClas methodCreator.load(setterName), methodCreator.readInstanceField(planningListVariableField, thisObj), methodCreator.invokeInterfaceMethod( - MethodDescriptor.ofMethod(PythonObject.class, "get__optapy_reference_map", Map.class), + MethodDescriptor.ofMethod(PythonObject.class, "get__timefold_reference_map", Map.class), thisObj), methodCreator.readInstanceField(pythonSetterField, thisObj)); } @@ -1338,24 +1338,24 @@ private static void generateWrapperMethods(ClassCreator classCreator, Class p FieldDescriptor pythonLikeValueMapField, FieldDescriptor pythonSetterField, FieldDescriptor typeField, - List> optaplannerMethodAnnotations) { - boolean hasOptaPyParentClass = false; + List> timefoldMethodAnnotations) { + boolean hasTimefoldParentClass = false; try { - parentClass.getMethod("get__optapy_Id", OpaquePythonReference.class); - hasOptaPyParentClass = true; + parentClass.getMethod("get__timefold_id", OpaquePythonReference.class); + hasTimefoldParentClass = true; } catch (NoSuchMethodException e) { // Do nothing } - if (!hasOptaPyParentClass) { + if (!hasTimefoldParentClass) { generateAsPointer(classCreator, valueField, referenceMapField, pythonLikeValueMapField, typeField); } - // We only need to create methods/fields for methods with OptaPlanner annotations - // optaplannerMethodAnnotations: list of tuples (methodName, returnType, annotationList) + // We only need to create methods/fields for methods with Timefold annotations + // timefoldMethodAnnotations: list of tuples (methodName, returnType, annotationList) // (Each annotation is represented by a Map) - List fieldDescriptorList = new ArrayList<>(optaplannerMethodAnnotations.size()); - List returnTypeList = new ArrayList<>(optaplannerMethodAnnotations.size()); + List fieldDescriptorList = new ArrayList<>(timefoldMethodAnnotations.size()); + List returnTypeList = new ArrayList<>(timefoldMethodAnnotations.size()); List planningEntityFieldList = new ArrayList<>(); List planningEntityCollectionFieldList = new ArrayList<>(); List planningEntityCollectionGetterList = new ArrayList<>(); @@ -1369,14 +1369,14 @@ private static void generateWrapperMethods(ClassCreator classCreator, Class p List planningScoreFieldList = new ArrayList<>(); List planningScoreSetterNameList = new ArrayList<>(); - for (List optaplannerMethodAnnotation : optaplannerMethodAnnotations) { - String methodName = (String) (optaplannerMethodAnnotation.get(0)); - Class returnType = (Class) (optaplannerMethodAnnotation.get(1)); - String signature = (String) (optaplannerMethodAnnotation.get(2)); + for (List timefoldMethodAnnotation : timefoldMethodAnnotations) { + String methodName = (String) (timefoldMethodAnnotation.get(0)); + Class returnType = (Class) (timefoldMethodAnnotation.get(1)); + String signature = (String) (timefoldMethodAnnotation.get(2)); if (returnType == null) { returnType = Object.class; } - List> annotations = (List>) optaplannerMethodAnnotation.get(3); + List> annotations = (List>) timefoldMethodAnnotation.get(3); fieldDescriptorList .add(generateWrapperMethod(classCreator, parentClass, valueField, pythonLikeValueMapField, pythonSetterField, @@ -1412,7 +1412,7 @@ private static void generateWrapperMethods(ClassCreator classCreator, Class p planningVariableFieldList, planningVariableSetterNameList, planningListVariableFieldList, planningListVariableSetterNameList); - if (!hasOptaPyParentClass) { + if (!hasTimefoldParentClass) { createToString(classCreator, valueField); } diff --git a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/TimefoldException.java b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/TimefoldException.java new file mode 100644 index 00000000..c32a8d78 --- /dev/null +++ b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/TimefoldException.java @@ -0,0 +1,13 @@ +package ai.timefold.solver.python; + +public class TimefoldException extends RuntimeException { + + public TimefoldException(String message) { + super(message); + } + + public TimefoldException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/OptaPyObjectReference.java b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/TimefoldObjectReference.java similarity index 82% rename from timefold-solver-python-core/src/main/java/ai/timefold/solver/python/OptaPyObjectReference.java rename to timefold-solver-python-core/src/main/java/ai/timefold/solver/python/TimefoldObjectReference.java index 79af6b63..62d4ad72 100644 --- a/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/OptaPyObjectReference.java +++ b/timefold-solver-python-core/src/main/java/ai/timefold/solver/python/TimefoldObjectReference.java @@ -5,10 +5,10 @@ import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.types.PythonLikeType; -public class OptaPyObjectReference implements PythonLikeObject { +public class TimefoldObjectReference implements PythonLikeObject { final long id; - public OptaPyObjectReference(long id) { + public TimefoldObjectReference(long id) { this.id = id; } @@ -44,7 +44,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - OptaPyObjectReference that = (OptaPyObjectReference) o; + TimefoldObjectReference that = (TimefoldObjectReference) o; return id == that.id; } @@ -55,7 +55,7 @@ public int hashCode() { @Override public String toString() { - return "OptaPyObjectReference{" + + return "TimefoldObjectReference{" + "id=" + id + '}'; } diff --git a/timefold-solver-python-core/src/main/python/__init__.py b/timefold-solver-python-core/src/main/python/__init__.py index 7b51935c..43e59d36 100644 --- a/timefold-solver-python-core/src/main/python/__init__.py +++ b/timefold-solver-python-core/src/main/python/__init__.py @@ -1,11 +1,11 @@ """ -This module wraps OptaPlanner and allow Python Objects +This module wraps Timefold and allow Python Objects to be used as the domain and Python functions to be used as the constraints. Using any decorators in this module will automatically start the JVM. If you want to pass custom arguments to the JVM, -use init before decorators and any optapy.types imports. +use init before decorators and any timefold.solver.types imports. """ from .annotations import * diff --git a/timefold-solver-python-core/src/main/python/annotations.py b/timefold-solver-python-core/src/main/python/annotations.py index 73d8c501..3a4cc65a 100644 --- a/timefold-solver-python-core/src/main/python/annotations.py +++ b/timefold-solver-python-core/src/main/python/annotations.py @@ -18,11 +18,11 @@ VariableListener as _VariableListener, PlanningVariableReference as _PlanningVariableReference """ -All OptaPlanner Python annotations work like this: +All Timefold Python annotations work like this: -1. Ensure OptaPy is init using ensure_init +1. Ensure Timefold Python module is initialized using ensure_init 2. Import the corresponding Java annotation -3. Set __optaplanner (ex: __optaplannerPlanningId) on the given function/class +3. Set __timefold_annotation_ (ex: __timefold_annotation_PlanningId) on the given function/class to a dict containing the following: - 'annotationType' -> the imported Java annotation - annotation parameter -> parameter value or None if unset @@ -47,7 +47,7 @@ def planning_id(getter_function: Callable[[], Union[int, str]]) -> Callable[[], """ ensure_init() from ai.timefold.solver.core.api.domain.lookup import PlanningId as JavaPlanningId - getter_function.__optaplannerPlanningId = { + getter_function.__timefold_annotation_PlanningId = { 'annotationType': JavaPlanningId } return getter_function @@ -71,10 +71,10 @@ def planning_pin(getter_function: Callable[[], bool]) -> Callable[[], bool]: """ ensure_init() from ai.timefold.solver.core.api.domain.entity import PlanningPin as JavaPlanningPin - getter_function.__optaplannerPlanningId = { + getter_function.__timefold_annotation_PlanningPin = { 'annotationType': JavaPlanningPin } - getter_function.__optapy_return = get_class(bool) + getter_function.__timefold_return = get_class(bool) return getter_function @@ -101,7 +101,7 @@ def planning_variable(variable_type: Type, value_range_provider_refs: List[str], def planning_variable_function_wrapper(variable_getter_function: Callable[[], Any]): ensure_init() from ai.timefold.solver.core.api.domain.variable import PlanningVariable as JavaPlanningVariable - variable_getter_function.__optaplannerPlanningVariable = { + variable_getter_function.__timefold_annotation_PlanningVariable = { 'annotationType': JavaPlanningVariable, 'valueRangeProviderRefs': value_range_provider_refs, 'nullable': nullable, @@ -109,7 +109,7 @@ def planning_variable_function_wrapper(variable_getter_function: Callable[[], An 'strengthComparatorClass': strength_comparator_class, 'strengthWeightFactoryClass': strength_weight_factory_class } - variable_getter_function.__optapy_return = get_class(variable_type) + variable_getter_function.__timefold_return = get_class(variable_type) return variable_getter_function return planning_variable_function_wrapper @@ -150,13 +150,13 @@ def planning_list_variable_function_wrapper(variable_getter_function: Callable[[ from java.util import List as JavaList from ai.timefold.solver.python import PythonWrapperGenerator # noqa from ai.timefold.solver.core.api.domain.variable import PlanningListVariable as JavaPlanningListVariable - variable_getter_function.__optaplannerPlanningListVariable = { + variable_getter_function.__timefold_annotation_PlanningListVariable = { 'annotationType': JavaPlanningListVariable, 'valueRangeProviderRefs': value_range_provider_refs, } - variable_getter_function.__optapy_is_planning_clone = True - variable_getter_function.__optapy_return = JavaList - variable_getter_function.__optapy_signature = PythonWrapperGenerator.getCollectionSignature( + variable_getter_function.__timefold_is_planning_clone = True + variable_getter_function.__timefold_return = JavaList + variable_getter_function.__timefold_signature = PythonWrapperGenerator.getCollectionSignature( JavaList, get_class(variable_type)) return variable_getter_function @@ -197,17 +197,17 @@ def custom_shadow_variable_function_mapper(custom_variable_getter_function: Call ensure_init() from ai.timefold.solver.core.api.domain.variable import CustomShadowVariable as JavaCustomShadowVariable - custom_variable_getter_function.__optaplannerCustomShadowVariable = { + custom_variable_getter_function.__timefold_annotation_CustomShadowVariable = { 'annotationType': JavaCustomShadowVariable, 'sources': sources, 'variableListenerRef': variable_listener_ref, } if variable_listener_class is not None: - custom_variable_getter_function.__optaplannerCustomShadowVariable['variableListenerClass'] = \ + custom_variable_getter_function.__timefold_annotation_CustomShadowVariable['variableListenerClass'] = \ get_class(variable_listener_class) - custom_variable_getter_function.__optapy_return = get_class(shadow_variable_type) + custom_variable_getter_function.__timefold_return = get_class(shadow_variable_type) return custom_variable_getter_function return custom_shadow_variable_function_mapper @@ -230,11 +230,11 @@ def index_shadow_variable_function_mapper(index_getter_function: Callable[[], An ensure_init() from ai.timefold.solver.core.api.domain.variable import IndexShadowVariable as JavaIndexShadowVariable planning_variable_name = source_variable_name - index_getter_function.__optaplannerIndexShadowVariable = { + index_getter_function.__timefold_annotation_IndexShadowVariable = { 'annotationType': JavaIndexShadowVariable, 'sourceVariableName': planning_variable_name, } - index_getter_function.__optapy_return = get_class(anchor_type) + index_getter_function.__timefold_return = get_class(anchor_type) return index_getter_function return index_shadow_variable_function_mapper @@ -261,11 +261,11 @@ def anchor_shadow_variable_function_mapper(anchor_getter_function: Callable[[], ensure_init() from ai.timefold.solver.core.api.domain.variable import AnchorShadowVariable as JavaAnchorShadowVariable planning_variable_name = source_variable_name - anchor_getter_function.__optaplannerAnchorShadowVariable = { + anchor_getter_function.__timefold_annotation_AnchorShadowVariable = { 'annotationType': JavaAnchorShadowVariable, 'sourceVariableName': planning_variable_name, } - anchor_getter_function.__optapy_return = get_class(anchor_type) + anchor_getter_function.__timefold_return = get_class(anchor_type) return anchor_getter_function return anchor_shadow_variable_function_mapper @@ -305,15 +305,15 @@ def inverse_relation_shadow_variable_function_mapper(inverse_relation_getter_fun if the_source_type is None: the_source_type = SelfType planning_variable_name = source_variable_name - inverse_relation_getter_function.__optaplannerInverseRelationVariable = { + inverse_relation_getter_function.__timefold_annotation_InverseRelationVariable = { 'annotationType': JavaInverseRelationShadowVariable, 'sourceVariableName': planning_variable_name, } if is_singleton: - inverse_relation_getter_function.__optapy_return = the_source_type + inverse_relation_getter_function.__timefold_return = the_source_type else: - inverse_relation_getter_function.__optapy_return = Collection - inverse_relation_getter_function.__optapy_signature = PythonWrapperGenerator.getCollectionSignature( + inverse_relation_getter_function.__timefold_return = Collection + inverse_relation_getter_function.__timefold_signature = PythonWrapperGenerator.getCollectionSignature( Collection, get_class(the_source_type)) return inverse_relation_getter_function @@ -325,7 +325,7 @@ def __verify_is_problem_fact(type, problem_fact_type): # These built-in python types have direct java equivalents # and thus can be used in Lists without an illegal item on the stack return - if not hasattr(type, '__optapy_java_class'): + if not hasattr(type, '__timefold_java_class'): raise ValueError(f'{type} is not a @{problem_fact_type}. Maybe decorate {type} with ' f'@{problem_fact_type}?') @@ -347,8 +347,8 @@ def problem_fact_property_function_mapper(getter_function: Callable[[], Any]): from ai.timefold.solver.core.api.domain.solution import \ ProblemFactProperty as JavaProblemFactProperty __verify_is_problem_fact(fact_type, 'problem_fact') - getter_function.__optapy_return = get_class(fact_type) - getter_function.__optaplannerPlanningEntityCollectionProperty = { + getter_function.__timefold_return = get_class(fact_type) + getter_function.__timefold_annotation_PlanningEntityCollectionProperty = { 'annotationType': JavaProblemFactProperty } return getter_function @@ -373,10 +373,10 @@ def problem_fact_collection_property_function_mapper(getter_function: Callable[[ from ai.timefold.solver.core.api.domain.solution import \ ProblemFactCollectionProperty as JavaProblemFactCollectionProperty __verify_is_problem_fact(fact_type, 'problem_fact') - getter_function.__optapy_return = JavaList - getter_function.__optapy_signature = PythonWrapperGenerator.getCollectionSignature( + getter_function.__timefold_return = JavaList + getter_function.__timefold_signature = PythonWrapperGenerator.getCollectionSignature( JavaList, get_class(fact_type)) - getter_function.__optaplannerPlanningEntityCollectionProperty = { + getter_function.__timefold_annotation_PlanningEntityCollectionProperty = { 'annotationType': JavaProblemFactCollectionProperty } return getter_function @@ -398,10 +398,10 @@ def planning_entity_property_function_mapper(getter_function: Callable[[], List] from ai.timefold.solver.core.api.domain.solution import \ PlanningEntityProperty as JavaPlanningEntityProperty __verify_is_problem_fact(entity_type, 'planning_entity') - getter_function.__optaplannerPlanningEntityCollectionProperty = { + getter_function.__timefold_annotation_PlanningEntityCollectionProperty = { 'annotationType': JavaPlanningEntityProperty } - getter_function.__optapy_return = get_class(entity_type) + getter_function.__timefold_return = get_class(entity_type) return getter_function return planning_entity_property_function_mapper @@ -422,11 +422,11 @@ def planning_entity_collection_property_function_mapper(getter_function: Callabl from ai.timefold.solver.core.api.domain.solution import \ PlanningEntityCollectionProperty as JavaPlanningEntityCollectionProperty __verify_is_problem_fact(entity_type, 'planning_entity') - getter_function.__optaplannerPlanningEntityCollectionProperty = { + getter_function.__timefold_annotation_PlanningEntityCollectionProperty = { 'annotationType': JavaPlanningEntityCollectionProperty } - getter_function.__optapy_return = JavaList - getter_function.__optapy_signature = PythonWrapperGenerator.getCollectionSignature( + getter_function.__timefold_return = JavaList + getter_function.__timefold_signature = PythonWrapperGenerator.getCollectionSignature( JavaList, get_class(entity_type)) return getter_function @@ -461,20 +461,20 @@ def value_range_provider_function_wrapper(getter_function: Callable[[], Union[Li from java.util import List as JavaList from java.lang import Object as JavaObject - getter_function.__optaplannerValueRangeProvider = { + getter_function.__timefold_annotation_ValueRangeProvider = { 'annotationType': JavaValueRangeProvider, 'id': range_id } - if not hasattr(getter_function, '__optapy_return'): + if not hasattr(getter_function, '__timefold_return'): actual_value_range_type = get_class(value_range_type) if JavaValueRange.class_.isAssignableFrom(actual_value_range_type): - getter_function.__optapy_return = get_class(value_range_type) + getter_function.__timefold_return = get_class(value_range_type) else: if actual_value_range_type == JavaObject: - getter_function.__optapy_return = PythonWrapperGenerator.getArrayClass(OpaquePythonReference) + getter_function.__timefold_return = PythonWrapperGenerator.getArrayClass(OpaquePythonReference) else: - getter_function.__optapy_return = JavaList - getter_function.__optapy_signature = PythonWrapperGenerator.getCollectionSignature( + getter_function.__timefold_return = JavaList + getter_function.__timefold_signature = PythonWrapperGenerator.getCollectionSignature( JavaList, actual_value_range_type) return getter_function @@ -504,13 +504,13 @@ def planning_score(score_type: Type['_Score'], def planning_score_function_wrapper(getter_function): ensure_init() from ai.timefold.solver.core.api.domain.solution import PlanningScore as JavaPlanningScore - getter_function.__optaplannerPlanningScore = { + getter_function.__timefold_annotation_PlanningScore = { 'annotationType': JavaPlanningScore, 'bendableHardLevelsSize': bendable_hard_levels_size, 'bendableSoftLevelsSize': bendable_soft_levels_size, 'scoreDefinitionClass': score_definition_class } - getter_function.__optapy_return = get_class(score_type) + getter_function.__timefold_return = get_class(score_type) return getter_function return planning_score_function_wrapper @@ -559,10 +559,10 @@ def __init__(self, a_list): def planning_entity_wrapper(entity_class_argument): from jpyinterpreter import force_update_type out = JImplements('ai.timefold.jpyinterpreter.types.wrappers.OpaquePythonReference')(entity_class_argument) - out.__optapy_java_class = _generate_planning_entity_class(entity_class_argument, annotation_data) - out.__optapy_is_planning_clone = True + out.__timefold_java_class = _generate_planning_entity_class(entity_class_argument, annotation_data) + out.__timefold_is_planning_clone = True _add_shallow_copy_to_class(out) - force_update_type(out, out.__optapy_java_class.getField('$TYPE').get(None)) + force_update_type(out, out.__timefold_java_class.getField('$TYPE').get(None)) return out if entity_class: # Called as @planning_entity @@ -582,8 +582,8 @@ def problem_fact(fact_class: Type) -> Type: ensure_init() from jpyinterpreter import force_update_type out = JImplements('ai.timefold.jpyinterpreter.types.wrappers.OpaquePythonReference')(fact_class) - out.__optapy_java_class = _generate_problem_fact_class(fact_class) - force_update_type(out, out.__optapy_java_class.getField('$TYPE').get(None)) + out.__timefold_java_class = _generate_problem_fact_class(fact_class) + force_update_type(out, out.__timefold_java_class.getField('$TYPE').get(None)) return out @@ -616,11 +616,11 @@ def __init__(self, a_list): ensure_init() from jpyinterpreter import force_update_type out = JImplements('ai.timefold.jpyinterpreter.types.wrappers.OpaquePythonReference')(planning_solution_class) - out.__optapy_java_class = _generate_planning_solution_class(planning_solution_class) - out.__optapy_is_planning_solution = True - out.__optapy_is_planning_clone = True + out.__timefold_java_class = _generate_planning_solution_class(planning_solution_class) + out.__timefold_is_planning_solution = True + out.__timefold_is_planning_clone = True _add_shallow_copy_to_class(out) - force_update_type(out, out.__optapy_java_class.getField('$TYPE').get(None)) + force_update_type(out, out.__timefold_java_class.getField('$TYPE').get(None)) return out @@ -638,7 +638,7 @@ def deep_planning_clone(planning_clone_object: Union[Type, Callable]): :param planning_clone_object: The class or property that should be deep planning cloned. :return: planning_clone_object marked as being required for deep planning clone. """ - planning_clone_object.__optapy_is_planning_clone = True + planning_clone_object.__timefold_is_planning_clone = True if isinstance(planning_clone_object, type): _add_shallow_copy_to_class(planning_clone_object) return planning_clone_object @@ -676,7 +676,7 @@ def wrapped_constraint_provider(constraint_factory): finally: constraint_stream.convert_to_java = BytecodeTranslation.IF_POSSIBLE constraint_stream.all_translated_successfully = True - wrapped_constraint_provider.__optapy_java_class = _generate_constraint_provider_class(function, + wrapped_constraint_provider.__timefold_java_class = _generate_constraint_provider_class(function, wrapped_constraint_provider) return wrapped_constraint_provider @@ -698,7 +698,7 @@ def easy_score_calculator(easy_score_calculator_function: Callable[[Solution_], :rtype: Callable[[Solution_], '_Score'] """ ensure_init() - easy_score_calculator_function.__optapy_java_class = \ + easy_score_calculator_function.__timefold_java_class = \ _generate_easy_score_calculator_class(easy_score_calculator_function) return easy_score_calculator_function @@ -770,7 +770,7 @@ def resetWorkingSolution(self, workingSolution: Solution_, constraintMatchEnable setattr(incremental_score_calculator, method, JOverride()(method_on_class)) out = jpype.JImplements(base_interface)(incremental_score_calculator) - out.__optapy_java_class = _generate_incremental_score_calculator_class(out, constraint_match_aware) + out.__timefold_java_class = _generate_incremental_score_calculator_class(out, constraint_match_aware) return out @@ -886,7 +886,7 @@ def reset_working_solution(self, score_director): JOverride()(method_on_class)) out = jpype.JImplements(base_interface)(the_variable_listener_class) - out.__optapy_java_class = _generate_variable_listener_class(out) + out.__timefold_java_class = _generate_variable_listener_class(out) return out if variable_listener_class: # Called as @variable_listener @@ -922,11 +922,11 @@ def wrapper_doChange(self, solution, problem_change_director): run_id = id(problem_change_director) solution.forceUpdate() - reference_map = solution.get__optapy_reference_map() - python_setter = solution._optaplannerPythonSetter + reference_map = solution.get__timefold_reference_map() + python_setter = solution._timefoldPythonSetter - problem_change_director._set_instance_map(run_id, solution.get__optapy_reference_map()) - problem_change_director._set_update_function(run_id, solution._optaplannerPythonSetter) + problem_change_director._set_instance_map(run_id, solution.get__timefold_reference_map()) + problem_change_director._set_update_function(run_id, solution._timefoldPythonSetter) class_doChange(self, solution, problem_change_director) @@ -934,7 +934,7 @@ def wrapper_doChange(self, solution, problem_change_director): problem_change_director._unset_update_function(run_id) reference_map.clear() - getattr(solution, "$setFields")(solution.get__optapy_Id(), id(solution.get__optapy_Id()), reference_map, + getattr(solution, "$setFields")(solution.get__timefold_id(), id(solution.get__timefold_id()), reference_map, python_setter) setattr(problem_change_class, 'doChange', JOverride()(wrapper_doChange)) diff --git a/timefold-solver-python-core/src/main/python/jars/__init__.py b/timefold-solver-python-core/src/main/python/jars/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/timefold-solver-python-core/src/main/python/jpype_type_conversions.py b/timefold-solver-python-core/src/main/python/jpype_type_conversions.py index d21c1142..fffe198b 100644 --- a/timefold-solver-python-core/src/main/python/jpype_type_conversions.py +++ b/timefold-solver-python-core/src/main/python/jpype_type_conversions.py @@ -178,7 +178,7 @@ def test(self, argument1, argument2, argument3, argument4, argument5): def _has_java_class(item): if isinstance(item, (JObject, int, str, bool)): return True - if hasattr(type(item), '__optapy_java_class'): + if hasattr(type(item), '__timefold_java_class'): return True return False diff --git a/timefold-solver-python-core/src/main/python/timefold_api_wrappers.py b/timefold-solver-python-core/src/main/python/timefold_api_wrappers.py index d19896fe..e71f3343 100644 --- a/timefold-solver-python-core/src/main/python/timefold_api_wrappers.py +++ b/timefold-solver-python-core/src/main/python/timefold_api_wrappers.py @@ -42,7 +42,7 @@ def __init__(self, solver_config: '_SolverConfig'): self.problem_id_to_solver_run_ref_list = dict() self.only_use_java_setters = PythonSolver.onlyUseJavaSetters - def _optapy_debug_get_solver_runs_dicts(self): + def _timefold_debug_get_solver_runs_dicts(self): """ Internal method used for testing; do not use """ @@ -63,7 +63,7 @@ def problem_getter(the_problem_id): from copy import copy problem = copy(problem_function(the_problem_id)) solver_run_id = (id(self), the_problem_id) - problem._optapy_solver_run_id = solver_run_id + problem._timefold_solver_run_id = solver_run_id self.problem_id_to_solver_run_ref_list[the_problem_id] = [problem, problem] _setup_solver_run(solver_run_id, self.problem_id_to_solver_run_ref_list[the_problem_id]) PythonSolver.onlyUseJavaSetters = self.only_use_java_setters @@ -189,7 +189,7 @@ def _wrap_call(self, function, problem): def updateScore(self, solution): score = self._wrap_call(lambda wrapped_solution: self._java_updateScore(wrapped_solution), solution) for attr in dir(solution): - if hasattr(getattr(solution, attr), '__optaplannerPlanningScore'): + if hasattr(getattr(solution, attr), '__timefold_annotation_PlanningScore'): setter = f'set{attr[3:]}' getattr(solution, setter)(score) break @@ -308,7 +308,7 @@ def removeProblemFact(self, problemFact, problemFactConsumer): class _PythonScoreDirector: @JOverride(sticky=True, rename='_java_afterVariableChanged') def afterVariableChanged(self, entity, variable_name): - entity._optapy_change_variable(variable_name) + entity._timefold_change_variable(variable_name) self._java_afterVariableChanged(entity, variable_name) @@ -390,7 +390,7 @@ def __jclass_init__(self): pass @staticmethod - def _optapy_debug_get_solver_runs_dicts(): + def _timefold_debug_get_solver_runs_dicts(): """ Internal method used for testing; do not use """ @@ -408,7 +408,7 @@ def solve(self, problem): raise ValueError(f'A problem was not passed to solve (parameter problem was ({problem})). Maybe ' f'pass an instance of a class annotated with @planning_solution to solve?') - if not hasattr(type(problem), '__optapy_is_planning_solution'): + if not hasattr(type(problem), '__timefold_is_planning_solution'): raise ValueError(f'The problem ({problem}) is not an instance of a @planning_solution class. Maybe ' f'decorate the problem class ({type(problem)}) with @planning_solution?') @@ -416,7 +416,7 @@ def solve(self, problem): solver_run_id = (id(self), id(problem), _uuid1()) solver_run_ref_list = [problem, problem] object_class = get_class(type(problem)) - problem._optapy_solver_run_id = solver_run_id + problem._timefold_solver_run_id = solver_run_id wrapped_problem = PythonSolver.wrapProblem(object_class, problem) _setup_solver_run(solver_run_id, solver_run_ref_list) diff --git a/timefold-solver-python-core/src/main/python/timefold_java_interop.py b/timefold-solver-python-core/src/main/python/timefold_java_interop.py index 5d591d63..64b670e1 100644 --- a/timefold-solver-python-core/src/main/python/timefold_java_interop.py +++ b/timefold-solver-python-core/src/main/python/timefold_java_interop.py @@ -9,7 +9,7 @@ from typing import cast, List, Tuple, Type, TypeVar, Callable, Dict, Any, Union, TYPE_CHECKING import copy from collections.abc import Sequence, MutableSequence, Mapping, Set -from .timefold_python_logger import optapy_logger +from .timefold_python_logger import timefold_logger from .jpype_type_conversions import PythonSupplier, PythonFunction, PythonBiFunction, PythonTriFunction, \ ConstraintProviderFunction @@ -25,9 +25,9 @@ def extract_timefold_jars() -> list[str]: - """Extracts and return a list of OptaPy Java dependencies + """Extracts and return a list of timefold Java dependencies - Invoking this function extracts OptaPy Dependencies from the timefold.solver.jars module + Invoking this function extracts timefold Dependencies from the timefold.solver.jars module into a temporary directory and returns a list contains classpath entries for those dependencies. The temporary directory exists for the entire execution of the program. @@ -66,10 +66,10 @@ def _get_python_object_attribute(object_id, name): the_object = object_id python_object_getter = getattr(the_object, str(name)) if not callable(python_object_getter): - from ai.timefold.solver.python import OptaPyException # noqa + from ai.timefold.solver.python import TimefoldException # noqa error = (f'The attribute {name} on {object_id}is not callable (got {python_object_getter}, ' f'expecting a function). You might have overridden the function {name} with a value.') - raise OptaPyException(error) + raise TimefoldException(error) try: python_object = python_object_getter() if python_object is None: @@ -78,7 +78,7 @@ def _get_python_object_attribute(object_id, name): ai.timefold.solver.core.api.score.Score)): out = JObject(python_object, java.lang.Object) return out - elif hasattr(python_object_getter, '__optaplannerPlanningId'): + elif hasattr(python_object_getter, '__timefold_annotation_PlanningId'): return PythonComparable( JProxy(ai.timefold.jpyinterpreter.types.wrappers.OpaquePythonReference, inst=python_object, convert=True)) @@ -86,9 +86,9 @@ def _get_python_object_attribute(object_id, name): return JProxy(ai.timefold.jpyinterpreter.types.wrappers.OpaquePythonReference, inst=python_object, convert=True) except Exception as e: - from ai.timefold.solver.python import OptaPyException # noqa + from ai.timefold.solver.python import TimefoldException # noqa error = f'An exception occur when calling {str(name)} on {str(the_object)}: {str(e)}. Check the code.' - raise OptaPyException(error) + raise TimefoldException(error) def _get_python_array_to_id_array(the_object: List): @@ -118,7 +118,7 @@ def _set_python_object_attribute(object_id: int, name: str, value: Any) -> None: the_object = object_id the_value = value if isinstance(the_value, PythonObject): - the_value = value.get__optapy_Id() + the_value = value.get__timefold_id() elif isinstance(the_value, PythonObjectWrapper): the_value = value.getWrappedObject() getattr(the_object, str(name))(the_value) @@ -137,7 +137,7 @@ def _deep_clone_python_object(the_object: Any): import ai.timefold.jpyinterpreter.types.wrappers.OpaquePythonReference from ai.timefold.solver.python import PythonWrapperGenerator # noqa item = PythonWrapperGenerator.getPythonObject(the_object) - run_id = item._optapy_solver_run_id # noqa ; cannot use __ since then we cannot access it here + run_id = item._timefold_solver_run_id # noqa ; cannot use __ since then we cannot access it here the_clone = _planning_clone(item, dict()) # Only need to keep two references: the best solution, and the working solution @@ -152,7 +152,7 @@ def _is_deep_planning_clone(object): :param object: The object to check if it should be deep planning cloned. :return: True iff object should be deep planning cloned, False otherwise. """ - return hasattr(type(object), '__optapy_is_planning_clone') + return hasattr(type(object), '__timefold_is_planning_clone') def _planning_clone(item, memo): @@ -230,24 +230,24 @@ def _planning_clone(item, memo): for planning_clone_attribute_name in dir(planning_clone_type): planning_clone_attribute = getattr(planning_clone_type, planning_clone_attribute_name) if inspect.isfunction(planning_clone_attribute) and \ - hasattr(planning_clone_attribute, '__optapy_is_planning_clone'): + hasattr(planning_clone_attribute, '__timefold_is_planning_clone'): setter = f'set{planning_clone_attribute_name[3:]}' try: attribute_value = getattr(planning_clone, planning_clone_attribute_name)() except Exception as e: - from ai.timefold.solver.python import OptaPyException # noqa + from ai.timefold.solver.python import TimefoldException # noqa error = (f'An exception occur when getting the @deep_planning_clone property' f'{planning_clone_attribute_name} on object {str(item)}: {str(e)}') - raise OptaPyException(error) + raise TimefoldException(error) attribute_value_clone = _planning_clone(attribute_value, memo) try: getattr(planning_clone, setter)(attribute_value_clone) except AttributeError as e: - from ai.timefold.solver.python import OptaPyException # noqa + from ai.timefold.solver.python import TimefoldException # noqa error = (f'There is no corresponding setter {setter} for deep cloned property ' f'{planning_clone_attribute_name} on object {str(item)}. Maybe add a setter? ' f'Original exception: {str(e)}') - raise OptaPyException(error) + raise TimefoldException(error) return planning_clone @@ -398,7 +398,7 @@ def init(*args, path: List[str] = None, include_timefold_jars: bool = True, log_ def ensure_init(): """Start the JVM if it isn't started; does nothing otherwise - Used by OptaPy to start the JVM when needed by a method, so + Used by timefold to start the JVM when needed by a method, so users don't need to start the JVM themselves. :return: None @@ -420,7 +420,7 @@ def set_class_output_directory(path: pathlib.Path): """Maps solver run id to solution clones it references""" -def _optapy_error(item): +def _timefold_error(item): raise AttributeError @@ -453,12 +453,12 @@ def __jclass_init__(self): pass - def _optapy_change_variable(self, variable_name): + def _timefold_change_variable(self, variable_name): from ai.timefold.solver.python import PythonWrapperGenerator # noqa PythonWrapperGenerator.updateVariableFromPythonObject(self, variable_name) - def __optapy_lookup(self, attribute, default_fun, *args, **kwargs): + def __timefold_lookup(self, attribute, default_fun, *args, **kwargs): from ai.timefold.solver.python import PythonWrapperGenerator # noqa item = PythonWrapperGenerator.getPythonObject(self) args = _convert_args(args) @@ -471,199 +471,199 @@ def __optapy_lookup(self, attribute, default_fun, *args, **kwargs): # These are needed for Python bytecodes, which look directly at the type and ignore attribute shenanigans def __getattr__(self, name): - return self.__optapy_lookup('__getattr__', lambda item: getattr(item, name), name) + return self.__timefold_lookup('__getattr__', lambda item: getattr(item, name), name) def __setattr__(self, name, value): - return self.__optapy_lookup('__setattr__', lambda item: setattr(item, name, value), name, value) + return self.__timefold_lookup('__setattr__', lambda item: setattr(item, name, value), name, value) def __delattr__(self, name): - return self.__optapy_lookup('__delattr__', lambda item: delattr(item, name), name) + return self.__timefold_lookup('__delattr__', lambda item: delattr(item, name), name) def __lt__(self, other): - return self.__optapy_lookup('__lt__', lambda item: item < other, other) + return self.__timefold_lookup('__lt__', lambda item: item < other, other) def __le__(self, other): - return self.__optapy_lookup('__le__', lambda item: item <= other, other) + return self.__timefold_lookup('__le__', lambda item: item <= other, other) def __eq__(self, other): - return self.__optapy_lookup('__eq__', lambda item: item == other, other) + return self.__timefold_lookup('__eq__', lambda item: item == other, other) def __gt__(self, other): - return self.__optapy_lookup('__gt__', lambda item: item > other, other) + return self.__timefold_lookup('__gt__', lambda item: item > other, other) def __ge__(self, other): - return self.__optapy_lookup('__ge__', lambda item: item >= other, other) + return self.__timefold_lookup('__ge__', lambda item: item >= other, other) def __ne__(self, other): - return self.__optapy_lookup('__ne__', lambda item: item != other, other) + return self.__timefold_lookup('__ne__', lambda item: item != other, other) def __bool__(self): - return self.__optapy_lookup('__bool__', lambda item: bool(item)) + return self.__timefold_lookup('__bool__', lambda item: bool(item)) def __hash__(self): - return self.__optapy_lookup('__hash__', lambda item: hash(item)) + return self.__timefold_lookup('__hash__', lambda item: hash(item)) def __str__(self): - return self.__optapy_lookup('__str__', lambda item: str(item)) + return self.__timefold_lookup('__str__', lambda item: str(item)) def __repr__(self): - return self.__optapy_lookup('__repr__', lambda item: repr(item)) + return self.__timefold_lookup('__repr__', lambda item: repr(item)) def __len__(self): - return self.__optapy_lookup('__len__', lambda item: len(item)) + return self.__timefold_lookup('__len__', lambda item: len(item)) def __iter__(self): - return self.__optapy_lookup('__iter__', lambda item: iter(item)) + return self.__timefold_lookup('__iter__', lambda item: iter(item)) def __contains__(self, item): - return self.__optapy_lookup('__contains__', lambda container: item in container, item) + return self.__timefold_lookup('__contains__', lambda container: item in container, item) def __getitem__(self, key): - return self.__optapy_lookup('__getitem__', lambda container: container[key], key) + return self.__timefold_lookup('__getitem__', lambda container: container[key], key) def __setitem__(self, key, value): - return self.__optapy_lookup('__setitem__', _optapy_error, key, value) + return self.__timefold_lookup('__setitem__', _timefold_error, key, value) def __delitem__(self, key): - return self.__optapy_lookup('__delitem__', _optapy_error, key) + return self.__timefold_lookup('__delitem__', _timefold_error, key) def __add__(self, other): - return self.__optapy_lookup('__add__', lambda item: item + other, other) + return self.__timefold_lookup('__add__', lambda item: item + other, other) def __sub__(self, other): - return self.__optapy_lookup('__sub__', lambda item: item - other, other) + return self.__timefold_lookup('__sub__', lambda item: item - other, other) def __mul__(self, other): - return self.__optapy_lookup('__mul__', lambda item: item * other, other) + return self.__timefold_lookup('__mul__', lambda item: item * other, other) def __matmul__(self, other): - return self.__optapy_lookup('__matmul__', lambda item: item @ other, other) + return self.__timefold_lookup('__matmul__', lambda item: item @ other, other) def __truediv__(self, other): - return self.__optapy_lookup('__truediv__', lambda item: item / other, other) + return self.__timefold_lookup('__truediv__', lambda item: item / other, other) def __floordiv__(self, other): - return self.__optapy_lookup('__floordiv__', lambda item: item // other, other) + return self.__timefold_lookup('__floordiv__', lambda item: item // other, other) def __mod__(self, other): - return self.__optapy_lookup('__mod__', lambda item: item % other, other) + return self.__timefold_lookup('__mod__', lambda item: item % other, other) def __divmod__(self, other): - return self.__optapy_lookup('__divmod__', lambda item: divmod(item, other), other) + return self.__timefold_lookup('__divmod__', lambda item: divmod(item, other), other) def __pow__(self, other, *extra_args): - return self.__optapy_lookup('__pow__', lambda item: item ** other, other, *extra_args) + return self.__timefold_lookup('__pow__', lambda item: item ** other, other, *extra_args) def __lshift__(self, other): - return self.__optapy_lookup('__lshift__', lambda item: item << other, other) + return self.__timefold_lookup('__lshift__', lambda item: item << other, other) def __rshift__(self, other): - return self.__optapy_lookup('__rshift__', lambda item: item >> other, other) + return self.__timefold_lookup('__rshift__', lambda item: item >> other, other) def __and__(self, other): - return self.__optapy_lookup('__and__', lambda item: item and other, other) + return self.__timefold_lookup('__and__', lambda item: item and other, other) def __xor__(self, other): - return self.__optapy_lookup('__xor__', lambda item: item ^ other, other) + return self.__timefold_lookup('__xor__', lambda item: item ^ other, other) def __or__(self, other): - return self.__optapy_lookup('__or__', lambda item: item or other, other) + return self.__timefold_lookup('__or__', lambda item: item or other, other) def __radd__(self, other): - return self.__optapy_lookup('__radd__', lambda item: other + item, other) + return self.__timefold_lookup('__radd__', lambda item: other + item, other) def __rsub__(self, other): - return self.__optapy_lookup('__rsub__', lambda item: other - item, other) + return self.__timefold_lookup('__rsub__', lambda item: other - item, other) def __rmul__(self, other): - return self.__optapy_lookup('__rmul__', lambda item: other * item, other) + return self.__timefold_lookup('__rmul__', lambda item: other * item, other) def __rmatmul__(self, other): - return self.__optapy_lookup('__rmatmul__', lambda item: other @ item, other) + return self.__timefold_lookup('__rmatmul__', lambda item: other @ item, other) def __rtruediv__(self, other): - return self.__optapy_lookup('__rtruediv__', lambda item: other / item, other) + return self.__timefold_lookup('__rtruediv__', lambda item: other / item, other) def __rfloordiv__(self, other): - return self.__optapy_lookup('__rfloordiv__', lambda item: other // item, other) + return self.__timefold_lookup('__rfloordiv__', lambda item: other // item, other) def __rmod__(self, other): - return self.__optapy_lookup('__rmod__', lambda item: other % item, other) + return self.__timefold_lookup('__rmod__', lambda item: other % item, other) def __rdivmod__(self, other): - return self.__optapy_lookup('__rdivmod__', lambda item: divmod(other, item), other) + return self.__timefold_lookup('__rdivmod__', lambda item: divmod(other, item), other) def __rpow__(self, other, *extra_args): - return self.__optapy_lookup('__rpow__', lambda item: other ** item, other, *extra_args) + return self.__timefold_lookup('__rpow__', lambda item: other ** item, other, *extra_args) def __rlshift__(self, other): - return self.__optapy_lookup('__rlshift__', lambda item: other << item, other) + return self.__timefold_lookup('__rlshift__', lambda item: other << item, other) def __rrshift__(self, other): - return self.__optapy_lookup('__rrshift__', lambda item: other >> item, other) + return self.__timefold_lookup('__rrshift__', lambda item: other >> item, other) def __rand__(self, other): - return self.__optapy_lookup('__rand__', lambda item: other and item, other) + return self.__timefold_lookup('__rand__', lambda item: other and item, other) def __rxor__(self, other): - return self.__optapy_lookup('__rxor__', lambda item: other ^ item, other) + return self.__timefold_lookup('__rxor__', lambda item: other ^ item, other) def __ror__(self, other): - return self.__optapy_lookup('__ror__', lambda item: other or item, other) + return self.__timefold_lookup('__ror__', lambda item: other or item, other) def __iadd__(self, other): - return self.__optapy_lookup('__iadd__', _optapy_error, other) + return self.__timefold_lookup('__iadd__', _timefold_error, other) def __isub__(self, other): - return self.__optapy_lookup('__isub__', _optapy_error, other) + return self.__timefold_lookup('__isub__', _timefold_error, other) def __imul__(self, other): - return self.__optapy_lookup('__imul__', _optapy_error, other) + return self.__timefold_lookup('__imul__', _timefold_error, other) def __imatmul__(self, other): - return self.__optapy_lookup('__imatmul__', _optapy_error, other) + return self.__timefold_lookup('__imatmul__', _timefold_error, other) def __itruediv__(self, other): - return self.__optapy_lookup('__itruediv__', _optapy_error, other) + return self.__timefold_lookup('__itruediv__', _timefold_error, other) def __ifloordiv__(self, other): - return self.__optapy_lookup('__ifloordiv__', _optapy_error, other) + return self.__timefold_lookup('__ifloordiv__', _timefold_error, other) def __imod__(self, other): - return self.__optapy_lookup('__imod__', _optapy_error, other) + return self.__timefold_lookup('__imod__', _timefold_error, other) def __ipow__(self, other, *extra_args): - return self.__optapy_lookup('__ipow__', _optapy_error, other, *extra_args) + return self.__timefold_lookup('__ipow__', _timefold_error, other, *extra_args) def __ilshift__(self, other): - return self.__optapy_lookup('__ilshift__', _optapy_error, other) + return self.__timefold_lookup('__ilshift__', _timefold_error, other) def __irshift__(self, other): - return self.__optapy_lookup('__irshift__', _optapy_error, other) + return self.__timefold_lookup('__irshift__', _timefold_error, other) def __iand__(self, other): - return self.__optapy_lookup('__iand__', _optapy_error, other) + return self.__timefold_lookup('__iand__', _timefold_error, other) def __ixor__(self, other): - return self.__optapy_lookup('__ixor__', _optapy_error, other) + return self.__timefold_lookup('__ixor__', _timefold_error, other) def __ior__(self, other): - return self.__optapy_lookup('__ior__', _optapy_error, other) + return self.__timefold_lookup('__ior__', _timefold_error, other) def __neg__(self): - return self.__optapy_lookup('__neg__', lambda item: -item) + return self.__timefold_lookup('__neg__', lambda item: -item) def __pos__(self): - return self.__optapy_lookup('__pos__', lambda item: +item) + return self.__timefold_lookup('__pos__', lambda item: +item) def __abs__(self): - return self.__optapy_lookup('__neg__', lambda item: abs(item)) + return self.__timefold_lookup('__neg__', lambda item: abs(item)) def __invert__(self): - return self.__optapy_lookup('__invert__', lambda item: ~item) + return self.__timefold_lookup('__invert__', lambda item: ~item) def __call__(self, *args, **kwargs): - return self.__optapy_lookup('__call__', lambda item: item(*args, **kwargs), *args, **kwargs) + return self.__timefold_lookup('__call__', lambda item: item(*args, **kwargs), *args, **kwargs) # Class duplication, since JImplementationFor does not seem to follow/allow class inheritance @@ -682,7 +682,7 @@ class _PythonComparable: def __jclass_init__(self): pass - def __optapy_lookup(self, attribute, default_fun, *args, **kwargs): + def __timefold_lookup(self, attribute, default_fun, *args, **kwargs): from ai.timefold.solver.python import PythonWrapperGenerator # noqa item = PythonWrapperGenerator.getPythonObject(self) args = _convert_args(args) @@ -695,199 +695,199 @@ def __optapy_lookup(self, attribute, default_fun, *args, **kwargs): # These are needed for Python bytecodes, which look directly at the type and ignore attribute shenanigans def __getattr__(self, name): - return self.__optapy_lookup('__getattr__', lambda item: getattr(item, name), name) + return self.__timefold_lookup('__getattr__', lambda item: getattr(item, name), name) def __setattr__(self, name, value): - return self.__optapy_lookup('__setattr__', lambda item: setattr(item, name, value), name, value) + return self.__timefold_lookup('__setattr__', lambda item: setattr(item, name, value), name, value) def __delattr__(self, name): - return self.__optapy_lookup('__delattr__', lambda item: delattr(item, name), name) + return self.__timefold_lookup('__delattr__', lambda item: delattr(item, name), name) def __lt__(self, other): - return self.__optapy_lookup('__lt__', lambda item: item < other, other) + return self.__timefold_lookup('__lt__', lambda item: item < other, other) def __le__(self, other): - return self.__optapy_lookup('__le__', lambda item: item <= other, other) + return self.__timefold_lookup('__le__', lambda item: item <= other, other) def __eq__(self, other): - return self.__optapy_lookup('__eq__', lambda item: item == other, other) + return self.__timefold_lookup('__eq__', lambda item: item == other, other) def __gt__(self, other): - return self.__optapy_lookup('__gt__', lambda item: item > other, other) + return self.__timefold_lookup('__gt__', lambda item: item > other, other) def __ge__(self, other): - return self.__optapy_lookup('__ge__', lambda item: item >= other, other) + return self.__timefold_lookup('__ge__', lambda item: item >= other, other) def __ne__(self, other): - return self.__optapy_lookup('__ne__', lambda item: item != other, other) + return self.__timefold_lookup('__ne__', lambda item: item != other, other) def __bool__(self): - return self.__optapy_lookup('__bool__', lambda item: bool(item)) + return self.__timefold_lookup('__bool__', lambda item: bool(item)) def __hash__(self): - return self.__optapy_lookup('__hash__', lambda item: hash(item)) + return self.__timefold_lookup('__hash__', lambda item: hash(item)) def __str__(self): - return self.__optapy_lookup('__str__', lambda item: str(item)) + return self.__timefold_lookup('__str__', lambda item: str(item)) def __repr__(self): - return self.__optapy_lookup('__repr__', lambda item: repr(item)) + return self.__timefold_lookup('__repr__', lambda item: repr(item)) def __len__(self): - return self.__optapy_lookup('__len__', lambda item: len(item)) + return self.__timefold_lookup('__len__', lambda item: len(item)) def __iter__(self): - return self.__optapy_lookup('__iter__', lambda item: iter(item)) + return self.__timefold_lookup('__iter__', lambda item: iter(item)) def __contains__(self, item): - return self.__optapy_lookup('__contains__', lambda container: item in container, item) + return self.__timefold_lookup('__contains__', lambda container: item in container, item) def __getitem__(self, key): - return self.__optapy_lookup('__getitem__', lambda container: container[key], key) + return self.__timefold_lookup('__getitem__', lambda container: container[key], key) def __setitem__(self, key, value): - return self.__optapy_lookup('__setitem__', _optapy_error, key, value) + return self.__timefold_lookup('__setitem__', _timefold_error, key, value) def __delitem__(self, key): - return self.__optapy_lookup('__delitem__', _optapy_error, key) + return self.__timefold_lookup('__delitem__', _timefold_error, key) def __add__(self, other): - return self.__optapy_lookup('__add__', lambda item: item + other, other) + return self.__timefold_lookup('__add__', lambda item: item + other, other) def __sub__(self, other): - return self.__optapy_lookup('__sub__', lambda item: item - other, other) + return self.__timefold_lookup('__sub__', lambda item: item - other, other) def __mul__(self, other): - return self.__optapy_lookup('__mul__', lambda item: item * other, other) + return self.__timefold_lookup('__mul__', lambda item: item * other, other) def __matmul__(self, other): - return self.__optapy_lookup('__matmul__', lambda item: item @ other, other) + return self.__timefold_lookup('__matmul__', lambda item: item @ other, other) def __truediv__(self, other): - return self.__optapy_lookup('__truediv__', lambda item: item / other, other) + return self.__timefold_lookup('__truediv__', lambda item: item / other, other) def __floordiv__(self, other): - return self.__optapy_lookup('__floordiv__', lambda item: item // other, other) + return self.__timefold_lookup('__floordiv__', lambda item: item // other, other) def __mod__(self, other): - return self.__optapy_lookup('__mod__', lambda item: item % other, other) + return self.__timefold_lookup('__mod__', lambda item: item % other, other) def __divmod__(self, other): - return self.__optapy_lookup('__divmod__', lambda item: divmod(item, other), other) + return self.__timefold_lookup('__divmod__', lambda item: divmod(item, other), other) def __pow__(self, other, *extra_args): - return self.__optapy_lookup('__pow__', lambda item: item ** other, other, *extra_args) + return self.__timefold_lookup('__pow__', lambda item: item ** other, other, *extra_args) def __lshift__(self, other): - return self.__optapy_lookup('__lshift__', lambda item: item << other, other) + return self.__timefold_lookup('__lshift__', lambda item: item << other, other) def __rshift__(self, other): - return self.__optapy_lookup('__rshift__', lambda item: item >> other, other) + return self.__timefold_lookup('__rshift__', lambda item: item >> other, other) def __and__(self, other): - return self.__optapy_lookup('__and__', lambda item: item and other, other) + return self.__timefold_lookup('__and__', lambda item: item and other, other) def __xor__(self, other): - return self.__optapy_lookup('__xor__', lambda item: item ^ other, other) + return self.__timefold_lookup('__xor__', lambda item: item ^ other, other) def __or__(self, other): - return self.__optapy_lookup('__or__', lambda item: item or other, other) + return self.__timefold_lookup('__or__', lambda item: item or other, other) def __radd__(self, other): - return self.__optapy_lookup('__radd__', lambda item: other + item, other) + return self.__timefold_lookup('__radd__', lambda item: other + item, other) def __rsub__(self, other): - return self.__optapy_lookup('__rsub__', lambda item: other - item, other) + return self.__timefold_lookup('__rsub__', lambda item: other - item, other) def __rmul__(self, other): - return self.__optapy_lookup('__rmul__', lambda item: other * item, other) + return self.__timefold_lookup('__rmul__', lambda item: other * item, other) def __rmatmul__(self, other): - return self.__optapy_lookup('__rmatmul__', lambda item: other @ item, other) + return self.__timefold_lookup('__rmatmul__', lambda item: other @ item, other) def __rtruediv__(self, other): - return self.__optapy_lookup('__rtruediv__', lambda item: other / item, other) + return self.__timefold_lookup('__rtruediv__', lambda item: other / item, other) def __rfloordiv__(self, other): - return self.__optapy_lookup('__rfloordiv__', lambda item: other // item, other) + return self.__timefold_lookup('__rfloordiv__', lambda item: other // item, other) def __rmod__(self, other): - return self.__optapy_lookup('__rmod__', lambda item: other % item, other) + return self.__timefold_lookup('__rmod__', lambda item: other % item, other) def __rdivmod__(self, other): - return self.__optapy_lookup('__rdivmod__', lambda item: divmod(other, item), other) + return self.__timefold_lookup('__rdivmod__', lambda item: divmod(other, item), other) def __rpow__(self, other, *extra_args): - return self.__optapy_lookup('__rpow__', lambda item: other ** item, other, *extra_args) + return self.__timefold_lookup('__rpow__', lambda item: other ** item, other, *extra_args) def __rlshift__(self, other): - return self.__optapy_lookup('__rlshift__', lambda item: other << item, other) + return self.__timefold_lookup('__rlshift__', lambda item: other << item, other) def __rrshift__(self, other): - return self.__optapy_lookup('__rrshift__', lambda item: other >> item, other) + return self.__timefold_lookup('__rrshift__', lambda item: other >> item, other) def __rand__(self, other): - return self.__optapy_lookup('__rand__', lambda item: other and item, other) + return self.__timefold_lookup('__rand__', lambda item: other and item, other) def __rxor__(self, other): - return self.__optapy_lookup('__rxor__', lambda item: other ^ item, other) + return self.__timefold_lookup('__rxor__', lambda item: other ^ item, other) def __ror__(self, other): - return self.__optapy_lookup('__ror__', lambda item: other or item, other) + return self.__timefold_lookup('__ror__', lambda item: other or item, other) def __iadd__(self, other): - return self.__optapy_lookup('__iadd__', _optapy_error, other) + return self.__timefold_lookup('__iadd__', _timefold_error, other) def __isub__(self, other): - return self.__optapy_lookup('__isub__', _optapy_error, other) + return self.__timefold_lookup('__isub__', _timefold_error, other) def __imul__(self, other): - return self.__optapy_lookup('__imul__', _optapy_error, other) + return self.__timefold_lookup('__imul__', _timefold_error, other) def __imatmul__(self, other): - return self.__optapy_lookup('__imatmul__', _optapy_error, other) + return self.__timefold_lookup('__imatmul__', _timefold_error, other) def __itruediv__(self, other): - return self.__optapy_lookup('__itruediv__', _optapy_error, other) + return self.__timefold_lookup('__itruediv__', _timefold_error, other) def __ifloordiv__(self, other): - return self.__optapy_lookup('__ifloordiv__', _optapy_error, other) + return self.__timefold_lookup('__ifloordiv__', _timefold_error, other) def __imod__(self, other): - return self.__optapy_lookup('__imod__', _optapy_error, other) + return self.__timefold_lookup('__imod__', _timefold_error, other) def __ipow__(self, other, *extra_args): - return self.__optapy_lookup('__ipow__', _optapy_error, other, *extra_args) + return self.__timefold_lookup('__ipow__', _timefold_error, other, *extra_args) def __ilshift__(self, other): - return self.__optapy_lookup('__ilshift__', _optapy_error, other) + return self.__timefold_lookup('__ilshift__', _timefold_error, other) def __irshift__(self, other): - return self.__optapy_lookup('__irshift__', _optapy_error, other) + return self.__timefold_lookup('__irshift__', _timefold_error, other) def __iand__(self, other): - return self.__optapy_lookup('__iand__', _optapy_error, other) + return self.__timefold_lookup('__iand__', _timefold_error, other) def __ixor__(self, other): - return self.__optapy_lookup('__ixor__', _optapy_error, other) + return self.__timefold_lookup('__ixor__', _timefold_error, other) def __ior__(self, other): - return self.__optapy_lookup('__ior__', _optapy_error, other) + return self.__timefold_lookup('__ior__', _timefold_error, other) def __neg__(self): - return self.__optapy_lookup('__neg__', lambda item: -item) + return self.__timefold_lookup('__neg__', lambda item: -item) def __pos__(self): - return self.__optapy_lookup('__pos__', lambda item: +item) + return self.__timefold_lookup('__pos__', lambda item: +item) def __abs__(self): - return self.__optapy_lookup('__neg__', lambda item: abs(item)) + return self.__timefold_lookup('__neg__', lambda item: abs(item)) def __invert__(self): - return self.__optapy_lookup('__invert__', lambda item: ~item) + return self.__timefold_lookup('__invert__', lambda item: ~item) def __call__(self, *args, **kwargs): - return self.__optapy_lookup('__call__', lambda item: item(*args, **kwargs), *args, **kwargs) + return self.__timefold_lookup('__call__', lambda item: item(*args, **kwargs), *args, **kwargs) def _add_shallow_copy_to_class(the_class: Type): @@ -936,7 +936,7 @@ def _cleanup_solver_run(solver_run_id): def _unwrap_java_object(java_object): """Gets the Python Python Object for the given Java Python Object""" - return java_object.get__optapy_Id() + return java_object.get__timefold_id() def _to_java_map(python_dict: Dict): @@ -969,21 +969,21 @@ def _to_java_list(python_list: List): return out -def _get_optaplanner_annotations(python_class: Type) -> List[Tuple[str, JClass, str, List[dict]]]: - """Gets the methods with OptaPlanner annotations in the given class""" +def _get_timefold_annotations(python_class: Type) -> List[Tuple[str, JClass, str, List[dict]]]: + """Gets the methods with timefold annotations in the given class""" method_list = [attribute for attribute in dir(python_class) if callable(getattr(python_class, attribute)) and attribute.startswith('__') is False] annotated_methods = [] for method in method_list: - optaplanner_annotations = [attribute for attribute in dir(getattr(python_class, method)) if - attribute.startswith('__optaplanner')] - if optaplanner_annotations: - return_type = getattr(getattr(python_class, method), "__optapy_return", None) - method_signature = getattr(getattr(python_class, method), "__optapy_signature", None) + timefold_annotations = [attribute for attribute in dir(getattr(python_class, method)) if + attribute.startswith('__timefold_annotation_')] + if timefold_annotations: + return_type = getattr(getattr(python_class, method), "__timefold_return", None) + method_signature = getattr(getattr(python_class, method), "__timefold_signature", None) annotated_methods.append( _to_java_list([method, return_type, method_signature, _to_java_list(list(map(lambda annotation: getattr(getattr(python_class, method), - annotation), optaplanner_annotations))) + annotation), timefold_annotations))) ])) return _to_java_list(annotated_methods) @@ -998,8 +998,8 @@ def get_class(python_class: Union[Type, Callable]) -> JClass: return cast(JClass, python_class) if isinstance(python_class, Class): return cast(JClass, python_class) - if hasattr(python_class, '__optapy_java_class'): - return python_class.__optapy_java_class + if hasattr(python_class, '__timefold_java_class'): + return python_class.__timefold_java_class if python_class == int: from java.lang import Integer return cast(JClass, Integer) @@ -1061,14 +1061,14 @@ def _generate_problem_fact_class(python_class): from ai.timefold.solver.python import PythonWrapperGenerator # noqa from jpyinterpreter import force_update_type class_identifier = _get_class_identifier_for_object(python_class) - optaplanner_annotations = _get_optaplanner_annotations(python_class) + timefold_annotations = _get_timefold_annotations(python_class) parent_class = compile_and_get_class(python_class) has_eq_and_hashcode = _does_class_define_eq_or_hashcode(python_class) out = PythonWrapperGenerator.defineProblemFactClass(_compose_unique_class_name(class_identifier), parent_class, has_eq_and_hashcode, - optaplanner_annotations) + timefold_annotations) class_identifier_to_java_class_map[class_identifier] = out force_update_type(python_class, out.getField('$TYPE').get(None)) return out @@ -1079,13 +1079,13 @@ def _generate_planning_entity_class(python_class: Type, annotation_data: Dict[st from ai.timefold.solver.python import PythonWrapperGenerator # noqa from jpyinterpreter import translate_python_class_to_java_class, force_update_type class_identifier = _get_class_identifier_for_object(python_class) - optaplanner_annotations = _get_optaplanner_annotations(python_class) + timefold_annotations = _get_timefold_annotations(python_class) parent_class = compile_and_get_class(python_class) has_eq_and_hashcode = _does_class_define_eq_or_hashcode(python_class) out = PythonWrapperGenerator.definePlanningEntityClass(_compose_unique_class_name(class_identifier), parent_class, has_eq_and_hashcode, - optaplanner_annotations, + timefold_annotations, _to_java_map(annotation_data)) class_identifier_to_java_class_map[class_identifier] = out force_update_type(python_class, out.getField('$TYPE').get(None)) @@ -1097,13 +1097,13 @@ def _generate_planning_solution_class(python_class: Type) -> JClass: from ai.timefold.solver.python import PythonWrapperGenerator # noqa from jpyinterpreter import translate_python_class_to_java_class, force_update_type class_identifier = _get_class_identifier_for_object(python_class) - optaplanner_annotations = _get_optaplanner_annotations(python_class) + timefold_annotations = _get_timefold_annotations(python_class) parent_class = compile_and_get_class(python_class) has_eq_and_hashcode = _does_class_define_eq_or_hashcode(python_class) out = PythonWrapperGenerator.definePlanningSolutionClass(_compose_unique_class_name(class_identifier), parent_class, has_eq_and_hashcode, - optaplanner_annotations) + timefold_annotations) class_identifier_to_java_class_map[class_identifier] = out force_update_type(python_class, out.getField('$TYPE').get(None)) return out diff --git a/timefold-solver-python-core/src/main/python/timefold_python_logger.py b/timefold-solver-python-core/src/main/python/timefold_python_logger.py index 960c641b..c00a9867 100644 --- a/timefold-solver-python-core/src/main/python/timefold_python_logger.py +++ b/timefold-solver-python-core/src/main/python/timefold_python_logger.py @@ -3,7 +3,7 @@ __original_logging_class = logging.getLoggerClass() -class OptaPyLogger(__original_logging_class): +class TimefoldLogger(__original_logging_class): def __init__(self, name): super().__init__(name) subpackage = name[len('timefold.solver'):] @@ -22,7 +22,7 @@ def getEffectiveLevel(self): PythonLoggingToLogbackAdapter.getEffectiveLevel(self.java_logger_name) def getChild(self, suffix): - return OptaPyLogger(f'{self.name}.{suffix}') + return TimefoldLogger(f'{self.name}.{suffix}') def addFilter(self, filter): raise NotImplementedError(f'Cannot add filter to {self.java_logger_name} logger') @@ -37,6 +37,6 @@ def removeHandler(self, hdlr): raise NotImplementedError(f'Cannot remove handler from {self.java_logger_name} logger') -logging.setLoggerClass(OptaPyLogger) -optapy_logger = logging.getLogger('timefold.solver') +logging.setLoggerClass(TimefoldLogger) +timefold_logger = logging.getLogger('timefold.solver') logging.setLoggerClass(__original_logging_class) diff --git a/timefold-solver-python-core/src/main/python/types/__init__.py b/timefold-solver-python-core/src/main/python/types/__init__.py index e4c90531..bafd11c3 100644 --- a/timefold-solver-python-core/src/main/python/types/__init__.py +++ b/timefold-solver-python-core/src/main/python/types/__init__.py @@ -1,5 +1,5 @@ """ -This module wraps Java types used in OptaPy. +This module wraps Java types used in Timefold. Importing anything from this module automatically starts the JVM. """ diff --git a/timefold/__init__.py b/timefold/__init__.py deleted file mode 100644 index a1f275a5..00000000 --- a/timefold/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .timefold import Placeholder diff --git a/timefold/timefold.py b/timefold/timefold.py deleted file mode 100644 index 0748c216..00000000 --- a/timefold/timefold.py +++ /dev/null @@ -1,9 +0,0 @@ -class Placeholder(): - def print(self): - '''Function to print information - Args: - None - Returns: - None - ''' - print("Timefold Solver Python is under development. We intend to release it in 2023.")