Implementing a Dynamic Cost Model for Resource Allocation
Many businesses need to model costs associated with resource usage. This challenge asks you to implement a flexible cost model in Python that can handle various resource types, usage metrics, and pricing structures. A well-designed cost model is crucial for accurate budgeting, pricing strategies, and resource optimization.
Problem Description
You are tasked with creating a CostModel class in Python. This class should allow users to define different resource types, associate them with specific usage metrics (e.g., CPU hours, data transfer, storage GB), and assign pricing rules to each resource. The model should then be able to calculate the total cost for a given set of resource usage data.
Key Requirements:
- Resource Definition: The
CostModelshould allow adding resources with a name, a usage metric (string), and a pricing rule (a function). - Pricing Rules: Pricing rules are functions that take the usage amount (a number) as input and return the cost for that usage. These functions can be simple (e.g., a fixed price per unit) or more complex (e.g., tiered pricing).
- Cost Calculation: The
CostModelshould have a method to calculate the total cost given a dictionary where keys are resource names and values are the usage amounts. - Flexibility: The design should be extensible to accommodate new resource types and pricing models easily.
Expected Behavior:
The CostModel class should be instantiated without arguments. It should provide methods to:
add_resource(self, resource_name, usage_metric, pricing_rule): Adds a new resource to the model.calculate_cost(self, usage_data): Calculates the total cost based on the provided usage data.
Edge Cases to Consider:
- Resource names in
usage_datathat are not defined in theCostModel. These should be ignored, and a warning message should be printed to the console. - Usage amounts that are negative. These should be treated as zero.
- Empty
usage_data. The total cost should be zero. - Pricing rules that raise exceptions (handle gracefully, potentially returning 0 cost for that resource).
Examples
Example 1:
Input:
cost_model = CostModel()
cost_model.add_resource("CPU", "hours", lambda x: 0.1 * x)
cost_model.add_resource("Storage", "GB", lambda x: 0.05 * x)
usage_data = {"CPU": 100, "Storage": 500, "Network": 20}
output = cost_model.calculate_cost(usage_data)
Output: 15.0
Explanation: CPU cost is 0.1 * 100 = 10.0, Storage cost is 0.05 * 500 = 25.0. Network is ignored. Total cost is 10.0 + 25.0 = 15.0
Example 2:
Input:
cost_model = CostModel()
cost_model.add_resource("Bandwidth", "GB", lambda x: 0.02 * x if x <= 1000 else 0.01 * x)
usage_data = {"Bandwidth": 1500}
output = cost_model.calculate_cost(usage_data)
Output: 30.0
Explanation: Bandwidth usage is 1500 GB. The first 1000 GB cost 0.02 * 1000 = 20.0. The remaining 500 GB cost 0.01 * 500 = 5.0. Total cost is 20.0 + 5.0 = 25.0
Example 3: (Edge Case)
Input:
cost_model = CostModel()
cost_model.add_resource("Memory", "GB", lambda x: 0.01 * x)
usage_data = {"Memory": -10, "Disk": 200}
output = cost_model.calculate_cost(usage_data)
Output: 0.0
Explanation: Memory usage is -10, which is treated as 0. Disk is ignored. Total cost is 0.0.
Constraints
- The
pricing_rulefunction must accept a single numerical argument (the usage amount) and return a numerical cost. - Resource names must be strings.
- Usage amounts must be numbers (int or float).
- The
calculate_costmethod should return a float representing the total cost. - The code should be well-documented and easy to understand.
Notes
- Consider using a dictionary to store the resources and their pricing rules.
- Error handling is important. Gracefully handle cases where a resource is not found or the pricing rule raises an exception.
- Think about how to make the
CostModelextensible to support more complex pricing models in the future (e.g., tiered pricing, volume discounts). - The warning message for unknown resources should be printed to
stderr(usingsys.stderr.write). This is standard practice for warnings. - You don't need to implement input validation beyond what's specified in the edge cases.