Skip to content

Conversation

@tsmbland
Copy link
Collaborator

@tsmbland tsmbland commented Oct 2, 2024

This PR combines a few small changes which I made while looking through the code and trying to understand how everything works. A lot of the changes are just to make the code more readable (e.g. adding inline comments), but there are a few changes to the actual code as well:

  • Remove some default values for function arguments. Specifically things like the year and forecast length. I think generally it's better to be explicit about these things, and I couldn't really see any reason you'd ever want these to rely on the defaults (apart from maybe making the tests simpler)

  • Remove agents.factories.factory (never used)

  • Remove demand_share.new_demand (never used)

  • Remove the protected argument from cliff_retirement_profile. The code was needlessly complex, and the equivalent can be done by clipping the input

  • Removing some unnecessary interpolation operations from Sector.next

  • Tidy up the Agent and InvestingAgent classes. I didn't like how a lot of the code for the InvestingAgent was wrapped up in the next method of the Agent class. I've tidied things up so that all the code related to investments is in the InvestingAgent class

  • aggregate_lp no longer returns anything. It's very difficult to tell when this function is supposed to output something, and what that output was supposed to be used for. Looking through the code, it appears this would only ever be used when using a noninvesting agent (Agent class rather than InvestingAgent), which is used when specifying the agent type as "agent", "default" or "standard". However, the only options given for this parameter in the documentation are "new" and "retrofit" (which both use the InvestingAgent class), so I can only assume these are legacy options, and this pathway isn't required any more (although maybe I'm missing something?). In which case, we can probably simplify the agents module even more that I've done here, bit I'll leave it for now. For what it's worth, the "trade" example model does use the "default" agent type, but isn't affected by these changes

This doesn't address any particular issues, but I'm increasingly finding that lack of readability is a barrier for bug-fixing and development, so I think any attempts to address this will be helpful in the long run, even if we end up breaking backwards-compatibility or undocumented features.

Probably should have broken this into multiple PRs, but I didn't really go into this with any sort of plan, so this is just how its ended up.

@tsmbland tsmbland changed the title Simplify code Small changes to simplify the code Oct 17, 2024
@tsmbland tsmbland changed the base branch from develop to v1.3 October 17, 2024 17:13
@tsmbland tsmbland changed the base branch from v1.3 to broadcast_errors2 October 25, 2024 13:37
Base automatically changed from broadcast_errors2 to v1.2.2 October 25, 2024 15:16
Base automatically changed from v1.2.2 to main October 28, 2024 08:46
@tsmbland tsmbland changed the title Small changes to simplify the code Simplify agents classes, and additional changes Oct 28, 2024
@tsmbland tsmbland marked this pull request as ready for review October 28, 2024 15:22
@tsmbland tsmbland changed the title Simplify agents classes, and additional changes Simplify the agents module, and additional changes Oct 28, 2024
@tsmbland tsmbland requested a review from dalonsoa October 28, 2024 15:22
# Calculates the demand divided by the number of assets times the number of agents
# if the demand is bigger than zero and the total demand assigned with the "method"
# function is zero.
unassigned = (demand / (len(shares) * len(summed_shares))).where(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reeeeeally bad idea to call len on a DataArray (summed_shares), as this will just take the length of the first dimension, which isn't guaranteed to be the dimension you're after ("asset" in this case), unless you're really careful about it. In this case we get away with it, but this has bitten me on another PR and took me a long time to figure out.

Also, I still don't really understand what this function is doing. Why is it multiplying the number of agents by the number of assets? What does totals represent?

Copy link
Collaborator

@dalonsoa dalonsoa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made a few comments and suggestions here and there, but everything seems to make sense to me.

It is illustrative of the state of MUSE that after all the code you have removed or changed, the tests are largely the same.

)


class Agent(AbstractAgent):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this is some sort of legacy feature not really used except, as you mention, by trade. But that's a very obscure sector that I've no idea how it works or what it does.


# Skip forward if the search space is empty
if any(u == 0 for u in search_space.shape):
getLogger(__name__).critical("Search space is empty")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the simulation continue after this sort of critical message? If it can, then it might not be that critical and just a warning is enough.

Copy link
Collaborator Author

@tsmbland tsmbland Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory it can continue, the agent just won't perform any investments for that year. If this happens, then it probably means there's a mistake with the way the model is defined, so this is something the user should definitely pay attention to. In any case, I'm a bit concerned that a lot a important messages are getting ignored, so have suggested these get outputted to a file (#498)

commodity=technologies.commodity, region=technologies.region
).interp(year=[2020, 2025])
assert all(agent.year == 2020 for agent in agents)
result = subsector.aggregate_lp(technologies, market)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this returning something different that None? I think this test only makes sense if aggregate_lp returns something different than None, which we have seen cannot happen. And if it can happen, as this test was demonstrating, under what conditions? Are we removing functionality that is actually useful (not necessarily in use)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using the Agent class, aggregate_lp would return the search space and constraints, and then these would be used to perform investments withing Subsector.invest. Conversely, the InvestingAgent performs investments within its own next method. In theory the net result should be the same, so I can't see any reason to use Agent over InvestingAgent. I'm guessing it's just a legacy thing that was never tidied up

@tsmbland tsmbland changed the base branch from main to v1.3 November 5, 2024 16:58
@tsmbland tsmbland merged commit 2cbaf72 into v1.3 Nov 5, 2024
14 checks passed
@tsmbland tsmbland deleted the refactor branch November 5, 2024 16:59
@tsmbland tsmbland mentioned this pull request Nov 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants