chriskelly / LifeFinances

Scripts for validating retirement plans using Monte Carlo analysis.
GNU Affero General Public License v3.0
9 stars 3 forks source link

Optimizer currently recursive, but doesn't need to be #120

Closed chriskelly closed 1 year ago

chriskelly commented 1 year ago

The main optimizer function, rather than looping, is calling itself, creating an unnecessary memory stack. The looping could be managed by an external controller function like below

def main(self):
        """Controls improvement loop.

        improvement_loop() cycles through mutating parameter sets, testing those sets,
        finding the best, and continuing to mutate.
        """
        try_earlier = False
        while True:
            command = self.improvement_loop(try_earlier)
            if command == 'stop':
                break
            elif command == 'improve':
                try_earlier = True
            elif command == 'restart':
                try_earlier = False

I started working on this, but ran into an issue of how best to deal with passing around the correct param_vals with decremented dates

def improvement_loop(self, try_earlier:bool) -> str:
        """Cycles through mutating parameter sets, testing those sets, finding the best, and
        continuing to mutate.

        User parameters are directly modified when a successful (success rate greater 
        than TARGET_SUCCESS_RATE) combination is found. Then the loop is restarted, but 
        any income profiles marked as 'try to optimize' are decremented by 0.25 years.

        Args:
            try_earlier : bool
                whether to reduce the eligible income profile end dates or not

        Returns:
            str: 'stop' (cancel optimizing), 'restart', or 'improve' (decrement income profile
            end dates)
        """
    # ---------------------- First parameter set ---------------------- #
        mute_param_vals = {param:val for param,val in self.model.param_vals.items()
                           if 'optimizable' in self.model.param_details[param]}
        full_param_vals = self.model.param_vals.copy() # Need to not modify the model version
        success_rate, parent_is_best_qty = 0.0 , 0
        if try_earlier:
            #TODO: perhaps reduce all the dates here instead of in model. In the model though,
            #       the database is updated as the dates are reduced. I don't want to do that
            #       until after we have a set verified as meeting the target success rate. Might 
            #       just update full_param_vals with the decremented dates, then have model run
            #       do the database work after verification. Could keep decrementation done in
            #       model, but it would be redundant. Other option is to pass in the full_param_vals
            #       and do the database update from those vals.