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

Using the same solver_manager_create for different input parameters #103

Open
bilics opened this issue Oct 7, 2022 · 7 comments
Open

Using the same solver_manager_create for different input parameters #103

bilics opened this issue Oct 7, 2022 · 7 comments

Comments

@bilics
Copy link

bilics commented Oct 7, 2022

Hello,

I have a question regarding the solver_manager_create - I would to like to be able to call an endpoint with specific routing parameters (different domain values for customers, demands, vehicles, etc) and receive a SINGLETON_ID to check upon statuses.

def solver_manager_create(solver_config: '_SolverConfig') -> '_SolverManager':
    """Creates a new SolverManager, which can be used to solve problems asynchronously (ex: Web requests).

So is it possible to update solver_config for multiple "tenants" using the same instance of _SolverManager (and update/get statuses using the SINGLETON_ID parameter)?

Or is it necessary to use something like Celery, to create independent/separate instances of _SolverManager for each call, with different parameters?)

Thanks!

@Christopher-Chianelli
Copy link
Contributor

That depends: by input parameters, are you talking about different problems or different SolverConfig?

If it for different problems (with the same SolverConfig), then that the purpose of SolverManager; you can use it to schedule solvers for asynchronous requests. There is one thing to be careful of though: do not use web frameworks that start a new process to handle a request. That will cause the Solver to be started in that process, but because that process only lives as long as the request, it will die (and additionally, the SolverManager in the main process will have no knowledge of the SolverManager in the subprocess).

If it for different problems with different SolverConfig (ex: different termination time), then you currently need to create seperate SolverFactory/SolverManager instance to handle each request. The related OptaPlanner issue is https://issues.redhat.com/browse/PLANNER-2663 ; Once that feature been implemented in OptaPlanner, it will be ported to OptaPy.

@bilics
Copy link
Author

bilics commented Oct 7, 2022

I imagine it is the same SolverConfig if I just to change the number of number of customers, their locations - and demands for each location will be different for each call.

In this case I would need to simply update the VehicleRoutingSolution with the appropriate values, correct?

There is one thing to be careful of though: do not use web frameworks that start a new process to handle a request. That will cause the Solver to be started in that process, but because that process only lives as long as the request, it will die (and additionally, the SolverManager in the main process will have no knowledge of the SolverManager in the subprocess).

Got it - thanks.

@Christopher-Chianelli
Copy link
Contributor

I imagine it is the same SolverConfig if I just to change the number of number of customers, their locations - and demands for each location will be different for each call.

In this case I would need to simply update the VehicleRoutingSolution with the appropriate values, correct?

Correct; just pass in a VehicleRoutingSolution instance with appropriate values for the tenant.

@bilics
Copy link
Author

bilics commented Oct 10, 2022

Thanks @Christopher-Chianelli - got it. Closing this.

@bilics bilics closed this as completed Oct 10, 2022
@bilics
Copy link
Author

bilics commented Oct 10, 2022

Hello, re-opening this because I don't think I understood completely how to setup/schedule the solver for multiple subsequent calls. Please find below the current code to test this scenario - where each call so SolveAndListen will act on a different set of config parameters.

vehicle_routing_solution = []
id_sequence = [1]
def generate_id():
    out = id_sequence[0]
    id_sequence[0] = out + 1
    return out

@app.route('/vrp/solve', methods=['POST'])
def solve():

    params = request.json
    customers = params['customers']
    vehicles = params['customers']
     
    global vehicle_routing_solution
    uid = generate_id() 

    solution = DataBuilder.builder().
                                 .set_vehicles(vehicles)\
                                 .set_customers(customers)\                                 
                                 .build()
    solution.set_score(HardSoftScore.ZERO)
    vehicle_routing_solution.append(solution)
        
    solver_manager.solveAndListen(uid, find_by_id, save, error_handler)
    
    return dict({ "status": uid})

def find_by_id(schedule_id):
    global vehicle_routing_solution
    return vehicle_routing_solution[schedule_id]

def save(solution):
    global vehicle_routing_solution
    # Not sure how to implement this function, since the UID is not available

def error_handler(problem_id, exception):
    print(f'an exception occurred solving {problem_id}: {exception.getMessage()}')
    exception.printStackTrace()

The above code yields an error that I can not identify the problem:

Traceback (most recent call last):
  File "/opt/homebrew/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "/opt/homebrew/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "/opt/homebrew/lib/python3.10/site-packages/optapy/optaplanner_api_wrappers.py", line 29, in await_best_solution_from_solver_job
    raise e
  File "/opt/homebrew/lib/python3.10/site-packages/optapy/optaplanner_api_wrappers.py", line 26, in await_best_solution_from_solver_job
    solver_job.getFinalBestSolution()
java.util.concurrent.java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: org.jpype.PyExceptionProxy

Thanks in advance @Christopher-Chianelli - if you have to point me in the right direction.

@bilics bilics reopened this Oct 10, 2022
@Christopher-Chianelli
Copy link
Contributor

Sadly, exception info is lost when setting a Python exception as the cause for a Java exception. Where is request defined (it is not a parameter for solve)? solve look correct; the issue is either in your constraints or domain model, but I would need to see them to say where.

@bilics
Copy link
Author

bilics commented Oct 14, 2022

Hi @Christopher-Chianelli - sorry about the delay - I have created a minimal example here:

https://github.com/bilics/optapy-test

The idea is to expose the solver via a REST API call:

curl --request POST \
  --url http://127.0.0.1:5002/vrp/solve \
  --header 'Content-Type: application/json' \
  --data '{
	"vehicles": 
 [
		{	"capacity": 25, "kind": 1, "depot":0},
		{	"capacity": 50, "kind": 2, "depot":0}
 ],
	"depots": 
 [
		{	"id": 1, "latitude": -19.3, "longitude": -42.1}
 ],
		"customers": 
 [
		{	"id": 1, "latitude": -23.1, "longitude": -46.243, "demand": 8},
	 	{	"id": 1, "latitude": -23.12, "longitude": -46.01264, "demand": 8},
	    {	"id": 1, "latitude": -23.14, "longitude": -46.1987, "demand": 8},
	   {	"id": 1, "latitude": -22.167, "longitude": -45.341, "demand": 8},
	 	{	"id": 1, "latitude": -21.1, "longitude": -46.843, "demand": 8},
	 	{	"id": 1, "latitude": -25.12, "longitude": -45.264, "demand": 8},
	{	"id": 1, "latitude": -23.74, "longitude": -46.6987, "demand": 8},
	{	"id": 1, "latitude": -22.467, "longitude": -45.241, "demand": 8},
	 	{	"id": 1, "latitude": -23.8, "longitude": -46.943, "demand": 8},
	 	{	"id": 1, "latitude": -23.9712, "longitude": -45.264, "demand": 8},
	{	"id": 1, "latitude": -23.154, "longitude": -44.2987, "demand": 8},
	{	"id": 1, "latitude": -21.167, "longitude": -46.141, "demand": 8}
 ]
}'

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants