Skip to content
New issue

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

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

Already on GitHub? Sign in 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

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in 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