Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Can't use generics in python planning entities #1281

Open
luxaritas opened this issue Dec 20, 2024 · 0 comments
Open

Can't use generics in python planning entities #1281

luxaritas opened this issue Dec 20, 2024 · 0 comments
Labels
bug Something isn't working process/needs triage Requires initial assessment of validity, priority etc.

Comments

@luxaritas
Copy link
Contributor

luxaritas commented Dec 20, 2024

Describe the bug
Attempting to make a planning entity class generic fails with a runtime error

Expected behavior
Solving succeeds just as it does without the generic

Actual behavior

Traceback (most recent call last):
  File "DefaultSolver.java", line 196, in ai.timefold.solver.core.impl.solver.DefaultSolver.solve
Exception: Java Exception

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/jonathan/Development/eterna/OpenKnotScorePipeline/.venv/lib/python3.12/site-packages/timefold/solver/_solver.py", line 109, in solve
    java_solution = self._delegate.solve(java_problem)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
java.lang.reflect.java.lang.reflect.MalformedParameterizedTypeException: java.lang.reflect.MalformedParameterizedTypeException: Mismatch of count of formal and actual type arguments in constructor of org.jpyinterpreter.user.__main__.ResourceConfiguration: 0 formal argument(s) 1 actual argument(s)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/jonathan/Development/eterna/OpenKnotScorePipeline/repro.py", line 51, in <module>
    solver_factory.build_solver().solve(problem)
  File "/home/jonathan/Development/eterna/OpenKnotScorePipeline/.venv/lib/python3.12/site-packages/timefold/solver/_solver.py", line 113, in solve
    raise RuntimeError(f'Solving failed due to an error: {e.getMessage()}.\n'
RuntimeError: Solving failed due to an error: Mismatch of count of formal and actual type arguments in constructor of org.jpyinterpreter.user.__main__.ResourceConfiguration: 0 formal argument(s) 1 actual argument(s).
Java stack trace: java.lang.reflect.MalformedParameterizedTypeException: Mismatch of count of formal and actual type arguments in constructor of org.jpyinterpreter.user.__main__.ResourceConfiguration: 0 formal argument(s) 1 actual argument(s)
        at java.base/sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.validateConstructorArguments(Unknown Source)
        at java.base/sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.<init>(Unknown Source)
        at java.base/sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.make(Unknown Source)
        at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeParameterizedType(Unknown Source)
        at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Unknown Source)
        at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(Unknown Source)
        at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Unknown Source)
        at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Unknown Source)
        at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(Unknown Source)
        at java.base/sun.reflect.generics.repository.MethodRepository.computeReturnType(Unknown Source)
        at java.base/sun.reflect.generics.repository.MethodRepository.getReturnType(Unknown Source)
        at java.base/java.lang.reflect.Method.getGenericReturnType(Unknown Source)
        at ai.timefold.solver.core.impl.domain.common.accessor.ReflectionBeanPropertyMemberAccessor.getGenericType(ReflectionBeanPropertyMemberAccessor.java:89)
        at ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor.visitEntitiesByEntityClass(SolutionDescriptor.java:973)
        at ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor.getApproximateValueCount(SolutionDescriptor.java:1080)
        at ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor.getProblemSizeStatistics(SolutionDescriptor.java:1143)
        at ai.timefold.solver.core.impl.solver.DefaultSolver.registerSolverSpecificMetrics(DefaultSolver.java:249)
        at ai.timefold.solver.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:228)
        at ai.timefold.solver.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:196)

To Reproduce

from typing import Annotated, Generic, TypeVar
from dataclasses import dataclass, field
from timefold.solver.domain import (
    PlanningVariable,
    PlanningEntityCollectionProperty,
    PlanningScore, ValueRangeProvider,
    planning_entity, planning_solution
)
from timefold.solver.score import HardSoftScore, constraint_provider
from timefold.solver import SolverFactory
from timefold.solver.config import SolverConfig, ScoreDirectorFactoryConfig, TerminationConfig, Duration

T = TypeVar('T')

@planning_entity
@dataclass
class ResourceConfiguration(Generic[T]):
    t: T

    cpus: Annotated[int, PlanningVariable(value_range_provider_refs = ['cpu_range'])] = 1

    def get_cpu_range(self) -> Annotated[list[int], ValueRangeProvider(id='cpu_range')]:
        return list(range(1, 5))

@planning_solution
@dataclass
class Schedule(Generic[T]):
    resource_configurations: Annotated[list[ResourceConfiguration[T]], PlanningEntityCollectionProperty]

    score: Annotated[HardSoftScore, PlanningScore] = field(default=None)

@constraint_provider
def constraints(factory):
    return [
        factory.for_each(ResourceConfiguration).penalize(HardSoftScore.ONE_HARD).as_constraint('static')
    ]

solver_factory = SolverFactory.create(
    SolverConfig(
        solution_class=Schedule,
        entity_class_list=[ResourceConfiguration],
        score_director_factory_config=ScoreDirectorFactoryConfig(
            constraint_provider_function=constraints
        ),
        termination_config=TerminationConfig(
            spent_limit=Duration(seconds=1)
        )
    )
)
problem = Schedule[int]([ResourceConfiguration[int](1)])
solver_factory.build_solver().solve(problem)

Environment

Timefold Solver Version or Git ref:
1.17.0

Output of java -version:

openjdk version "23" 2024-09-17
OpenJDK Runtime Environment (build 23)
OpenJDK 64-Bit Server VM (build 23, mixed mode, sharing)

Output of uname -a or ver:

Linux 6.6.63-1-MANJARO #1 SMP PREEMPT_DYNAMIC Sat Nov 23 02:15:34 UTC 2024 x86_64 GNU/Linux
@luxaritas luxaritas added bug Something isn't working process/needs triage Requires initial assessment of validity, priority etc. labels Dec 20, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug Something isn't working process/needs triage Requires initial assessment of validity, priority etc.
Projects
None yet
Development

No branches or pull requests

1 participant