Skip to content

Possible 85-95% reduction in stage 2 reweighting time using drop-in-replacement solver in solver.jl #381

@donboyd5

Description

@donboyd5

I don't know if others have found reweighting to be as slow as I have, but stage 2 reweighing for CPS for 17 years took more than 14 hours on an older but still reasonably fast CPU. That's more time than I can tie a computer up for ordinarily.

As some of us have discussed, this really shouldn't take more than half a minute or so a year with an NLP solver and slight problem restructuring.

But in this case I was looking for a pure LP solution that required no restructuring.

After looking over the list of available JuMP LP solvers, I decided to investigate Tulip (here and here).

After installing, it can be dropped into solver.jl with just the following code changes:

# Add Tulip to the packages used
using JuMP, Cbc, NPZ, Tulip  # drop Cbc if done testing
...

# Replace these next 2 lines:
# model = Model(Cbc.Optimizer)
# set_optimizer_attribute(model, "logLevel", 1)

# With these 3 lines:
model = Model(Tulip.Optimizer)  # djb
set_optimizer_attribute(model, "OutputLevel", 1)  # 0=disable output (default), 1=show iterations	
set_optimizer_attribute(model, "IPM_IterationsLimit", 500)  # default 100
...

# Add these 2 lines to see termination status:
st = termination_status(model)
println("Termination status: $st")
...

Here are summary results for 2014. As you can see Cbc takes a quarter-million iterations. For Tulip, I raised the default limit on number of iterations from 100 to 500 so that the solution values are almost identical to those from Cbc for 2014:

Objective function:
Cbc (taxdata): 34,003.997
Tulip (500 iter) 34,004.8231
Tulip (100 iter) 34,027.7230

Iterations:
Cbc (taxdata): 253,373
Tulip (500 iter) 500
Tulip (100 iter) 100

Solution time (minutes):
Cbc (taxdata): 48.8
Tulip (500 iter) 6.4
Tulip (100 iter) 1.3

Quantiles of the key result (ratio of new weight to old weight), (0, .1, .25, .5, .75, .9, 1):
Cbc: [0.3, 1. , 1. , 1. , 1. , 1.7]
Tulip (500 iter) [0.3 0.99999999, 1. , 1. 1. , 1.7]
Tulip (100 iter) [0.30000021, 0.99998908, 0.99999819, 1. , 1.00000274, 1.69999996]

Correlations of the key result (ratio of new weight to old weight):
Cbc compared to Tulip with 500 iterations:
array([[1. , 0.9987413],
[0.9987413, 1. ]])

Cbc compared to Tulip with 100 iterations:
array([[1. , 0.99372129],
[0.99372129, 1. ]])

I did this for 2015, also, and results were similar. I have not looked at other years.

Tulip does not use a lot of memory, so that will not be a consideration on most machines.

Conclusions
It seems pretty clear to me that you can have a massive speedup with a near-drop-in-replacement and it might be worth considering. Tulip did not reach optimality at 500 iterations but the objective function was only 0.002% higher than that of Cbc. You might be able to get Tulip closer to the Cbc optimal result by varying one or more options, but it doesn't seem worth a lot of effort. Personally, I'd consider the result from 100 iterations from Tulip great for testing purposes and would only run more iterations for creation of a production file.

NOTE: When I say Tulip did not reach optimality, I mean that it did not think it had reached the minimum objective function -- the sum of (absolute differences between the ratio of new weight to old weight, minus 1). But more important, it satisfied the constraints very early on, within its tolerances -- that is, constraints for wages by income range, etc., all were met (and FAR sooner than Cbc met them).

You might be tempted to run Cbc for fewer iterations but looking at the output log, its results seem far from acceptable even at 200,000 iterations. It is not until it gets close to 250,000 iterations that the solution looks really good. (Obviously this varies from year to year, but the general pattern remains.)

I am a newcomer to Julia, JuMP, and Tulip. There may well be ways to speed it up further, but it is pretty good out of the box.

Obviously I would not recommend it as a replacement for Cbc without further testing. Sometimes one solver finds a particular problem harder than another solver and it's always possible that it will not be robust enough. Still based on my checking so far, it looks like a much better solver for taxdata purposes than Cbc and I'd recommend testing and probably deploying it. I plan to use it in my use of taxdata.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions