Skip to content

Commit

Permalink
Merge branch 'main' into python-to-java-bytecode
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher-Chianelli committed Sep 2, 2022
2 parents c9be61f + 4b6b274 commit 196c009
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 3 deletions.
6 changes: 5 additions & 1 deletion optapy-core/src/main/python/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,13 @@ def custom_shadow_variable_function_mapper(custom_variable_getter_function: Call
custom_variable_getter_function.__optaplannerCustomShadowVariable = {
'annotationType': JavaCustomShadowVariable,
'sources': sources,
'variableListenerClass': get_class(variable_listener_class),
'variableListenerRef': variable_listener_ref,
}

if variable_listener_class is not None:
custom_variable_getter_function.__optaplannerCustomShadowVariable['variableListenerClass'] = \
get_class(variable_listener_class)

custom_variable_getter_function.__optapy_return = get_class(shadow_variable_type)
return custom_variable_getter_function

Expand Down
8 changes: 6 additions & 2 deletions optapy-core/src/main/python/optaplanner_java_interop.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,8 +934,10 @@ def _to_java_map(python_dict: Dict):
import java.util.HashMap
out = java.util.HashMap()
for key, value in python_dict.items():
if isinstance(value, list):
if isinstance(value, (list, tuple)):
out.put(JObject(key, java.lang.Object), _to_java_list(value).toArray())
elif isinstance(value, Mapping):
out.put(JObject(key, java.lang.Object), _to_java_map(value))
else:
out.put(JObject(key, java.lang.Object), JObject(value, java.lang.Object))
return out
Expand All @@ -947,7 +949,9 @@ def _to_java_list(python_list: List):
import java.util.ArrayList
out = java.util.ArrayList()
for item in python_list:
if isinstance(item, dict):
if isinstance(item, (list, tuple)):
out.add(_to_java_list(item))
elif isinstance(item, Mapping):
out.add(_to_java_map(item))
else:
out.add(JObject(item, java.lang.Object))
Expand Down
120 changes: 120 additions & 0 deletions tests/test_custom_shadow_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,123 @@ def set_score(self, score):
assert solution.score.getScore() == 1
assert solution.entity_list[0].value == 2
assert solution.entity_list[0].value_squared == 4


def test_custom_shadow_variable_with_variable_listener_ref():
@optapy.variable_listener
class MyVariableListener:
def afterVariableChanged(self, score_director: ScoreDirector, entity: 'MyPlanningEntity'):
score_director.beforeVariableChanged(entity, 'twice_value')
score_director.beforeVariableChanged(entity, 'value_squared')
if entity.value is None:
entity.twice_value = None
entity.value_squared = None
else:
entity.twice_value = 2 * entity.value
entity.value_squared = entity.value ** 2
score_director.afterVariableChanged(entity, 'value_squared')
score_director.afterVariableChanged(entity, 'twice_value')

def beforeVariableChanged(self, score_director: ScoreDirector, entity: 'MyPlanningEntity'):
pass

def beforeEntityAdded(self, score_director: ScoreDirector, entity: 'MyPlanningEntity'):
pass

def afterEntityAdded(self, score_director: ScoreDirector, entity: 'MyPlanningEntity'):
pass

def beforeEntityRemoved(self, score_director: ScoreDirector, entity: 'MyPlanningEntity'):
pass

def afterEntityRemoved(self, score_director: ScoreDirector, entity: 'MyPlanningEntity'):
pass

@optapy.planning_entity
class MyPlanningEntity:
value: int
value_squared: int
twice_value: int

def __init__(self):
self.value = None
self.value_squared = None
self.twice_value = None

@optapy.planning_variable(int, value_range_provider_refs=['value_range'])
def get_value(self):
return self.value

def set_value(self, new_value):
self.value = new_value

@optapy.custom_shadow_variable(int, variable_listener_class=MyVariableListener,
sources=[optapy.planning_variable_reference('value')])
def get_value_squared(self):
return self.value_squared

def set_value_squared(self, new_value_squared):
self.value_squared = new_value_squared

@optapy.custom_shadow_variable(int, variable_listener_ref=optapy.planning_variable_reference('value_squared'))
def get_twice_value(self):
return self.twice_value

def set_twice_value(self, twice_value):
self.twice_value = twice_value

@optapy.constraint_provider
def my_constraints(constraint_factory: optapy.constraint.ConstraintFactory):
return [
constraint_factory.for_each(MyPlanningEntity)
.filter(lambda entity: entity.twice_value == entity.value_squared)
.reward('Double value is value squared', optapy.score.SimpleScore.ONE)
]

@optapy.planning_solution
class MySolution:
entity_list: list[MyPlanningEntity]
value_list: list[int]
score: optapy.score.SimpleScore

def __init__(self, entity_list, value_list, score=None):
self.entity_list = entity_list
self.value_list = value_list
self.score = score

@optapy.planning_entity_collection_property(MyPlanningEntity)
def get_entity_list(self):
return self.entity_list

def set_entity_list(self, entity_list):
self.entity_list = entity_list

@optapy.problem_fact_collection_property(int)
@optapy.value_range_provider('value_range')
def get_value_list(self):
return self.value_list

def set_value_list(self, value_list):
self.value_list = value_list

@optapy.planning_score(optapy.score.SimpleScore)
def get_score(self):
return self.score

def set_score(self, score):
self.score = score

solver_config = optapy.config.solver.SolverConfig() \
.withSolutionClass(MySolution) \
.withEntityClasses(MyPlanningEntity) \
.withConstraintProviderClass(my_constraints) \
.withTerminationConfig(optapy.config.solver.termination.TerminationConfig()
.withBestScoreLimit('1'))

solver_factory = optapy.solver_factory_create(solver_config)
solver = solver_factory.buildSolver()
problem = MySolution([MyPlanningEntity()], [1, 2, 3])
solution: MySolution = solver.solve(problem)
assert solution.score.getScore() == 1
assert solution.entity_list[0].value == 2
assert solution.entity_list[0].value_squared == 4

0 comments on commit 196c009

Please sign in to comment.