Delivery price depending on order value with multiple thresholds - python

Suppose we purchase N products from different vendors. We have many suppliers, but not all products are available from every supplier. The price for each product is known to each retailer, unless the product is not available there. There is also a shipping fee for each seller. The shipping fee is payable only once for all orders from a given supplier. It may also be subject to special restrictions, e.g. if the total order value exceeds 179USD, shipping is free, otherwise shipping is 15.99 USD or it is impossible if it does not meet the minimum order value, e.g. 100 USD. The shipping price for each seller may vary. Based on the products selected and the pricing and shipping information from different vendors, how can we optimize the total cost (product price + shipping) for the entire product cart, assuming we always want to buy all products?
Currently, I can solve this problem for fixed delivery costs for each supplier, but I cannot cope with adding conditional constraints taking into account the value of the order with a given supplier, e.g .:
order> = 179 PLN = free delivery
order <179 PLN AND order> = 100 PLN = delivery 15.99 PLN
minimum order = PLN 100 (below this amount, the order is not possible with the given supplier.
As for the structure of data on delivery costs, the 'key' means the minimum amount from which the 'value', i.e. delivery cost, is met for a given supplier. If the cost of a product under 'product_prices' for a given supplier is 99999999, it means that the product is not available from them. Below is the example data:
'delivery_prices': {
'supplier1': {
100: 14.9,
},
'supplier2': {
0: 19.99,
179: 0,
},
'supplier3': {
100: 15,
200: 10,
250: 0,
},
}
Here is my optimization code as for now:
import pyomo.environ as pe
import pyomo.opt as po
solver = po.SolverFactory('glpk')
def optimization_problem(input_data, solver):
model = pe.ConcreteModel("All products filled")
# sets
model.I = pe.Set(initialize=input_data['supplier_names'])
model.J = pe.Set(initialize=input_data['product_names'])
# parameters
model.s = pe.Param(model.I, initialize=input_data['supplier_stock'])
model.d = pe.Param(model.J, initialize=input_data['demand'])
model.f = pe.Param(model.I, initialize=input_data['delivery_prices'])
model.c = pe.Param(model.I, model.J, initialize=input_data['product_prices'])
# variables
model.x = pe.Var(model.I, model.J, domain=pe.NonNegativeReals)
model.y = pe.Var(model.I, domain=pe.Binary) # product quantity for given suppliersupplier
# constraints
# if sum
def con_satisfaction(model, j):
return sum(model.x[i, j] for i in model.I) >= model.d[j]
model.con_satisfaction = pe.Constraint(model.J, rule=con_satisfaction)
def con_transportation(model, i):
return sum(model.x[i, j] for j in model.J) <= model.s[i] * model.y[i]
model.con_transportation = pe.Constraint(model.I, rule=con_transportation)
# objective
def obj_min_cost(model):
return sum(model.f[i] * model.y[i] for i in model.I)\
+ sum(model.c[i, j] * model.x[i, j] for i in model.I for j in model.J)
model.obj_min_cost = pe.Objective(sense=pe.minimize, rule=obj_min_cost)
solver.solve(model)
products_distribution = { key: value for key, value in model.x.extract_values().items() if value > 0}
products_suppliers = list(set([product[0] for product in products_distribution.keys()]))
suppliers_delivery_prices = {supplier:price for supplier, price in input_data['delivery_prices'].items() if supplier in products_suppliers}
return (model.obj_min_cost(), products_suppliers, suppliers_delivery_prices, products_distribution)
# EXECUTE FUNCTION
optimization_problem(example_dataset, solver)
Does anyone have any guidance how to incorporate conditional delivery prices?

Here is a working example with the introduction of a binary variable, indexed by supplier and tier. Also added are 2 constraints to link the tier to the lower and upper bounds on qty from a supplier (linking constraints).
import pyomo.environ as pe
solver = pe.SolverFactory('glpk')
s1, s2 = 'supplier 1', 'supplier 2'
p1, p2 = 'bike', 'cellphone'
# some fake data
input_data = { 'supplier_names' : [s1, s2],
'product_names' : [p1, p2],
'supplier_stock' : {(s1, p1): 1000, (s1, p2): 500, (s2, p1): 700, (s2, p2): 400},
'demand' : {p1: 50, p2: 80},
'product_prices' : {(s1, p1): 100, (s1, p2): 150, (s2, p1): 90, (s2, p2): 160}
}
max_products = 10000 # some reasonable large "big M" upper bound on the total number of products from a supplier
penalty = 10000 # some reasonably large penalty...
# supplier tier (qty, price)
delivery_prices = { s1: {'below min' : (0, penalty), # <100 N/A, use penalty
'1A' : (100, 19.99),
'1B' : (200, 0.00)},
s2: {'regular' : (0, 25.00),
'free' : (150, 0.00)} }
# make a convenience set of all the tier label possiblities accross all suppliers...
all_tiers = set()
for k in delivery_prices:
all_tiers.update(set(delivery_prices[k].keys()))
model = pe.ConcreteModel("All products filled")
# sets
model.suppliers = pe.Set(initialize=input_data['supplier_names'])
model.products = pe.Set(initialize=input_data['product_names'])
model.tiers = pe.Set(initialize=list(all_tiers))
model.supplier_tiers = pe.Set(within=model.suppliers * model.tiers, initialize={(s,t) for s in delivery_prices
for t in delivery_prices[s]})
# parameters
model.s = pe.Param(model.suppliers, model.products, initialize=input_data['supplier_stock'])
model.d = pe.Param(model.products, initialize=input_data['demand'])
model.c = pe.Param(model.suppliers, model.products, initialize=input_data['product_prices'])
# variables
model.x = pe.Var(model.suppliers, model.products, domain=pe.NonNegativeReals) # product quantity for given suppliersupplier
model.y = pe.Var(model.supplier_tiers, domain=pe.Binary) # buy from supplier s at tier t
# constraints
# link quantity to supplier tier LB
def tier(model, supplier, tier):
return sum(model.x[supplier, p] for p in model.products) >= model.y[supplier, tier]*delivery_prices[supplier][tier][0]
model.tier_min = pe.Constraint(model.supplier_tiers, rule=tier)
# link quantity to supplier UB... this forces selection of some tier to get anything from supplier
def upper(model, supplier):
return sum(model.x[supplier, p] for p in model.products) <= sum(model.y[supplier, tier] for tier in model.tiers
if (supplier, tier) in model.supplier_tiers) * max_products
model.upper_bound = pe.Constraint(model.suppliers, rule=upper)
# meet demand
def demand(model, product):
return sum(model.x[supplier, product] for supplier in model.suppliers) >= model.d[product]
model.demand = pe.Constraint(model.products, rule=demand)
# objective
def obj_min_cost(model):
return sum(model.x[s, p] * model.c[s, p] for s in model.suppliers for p in model.products) + \
sum(model.y[s, t] * delivery_prices[s][t][1] for s,t in model.supplier_tiers)
model.obj_min_cost = pe.Objective(sense=pe.minimize, rule=obj_min_cost)
result = solver.solve(model)
print(result)
model.display()
# products_distribution = { key: value for key, value in model.x.extract_values().items() if value > 0}
# products_suppliers = list(set([product[0] for product in products_distribution.keys()]))
# suppliers_delivery_prices = {supplier:price for supplier, price in input_data['delivery_prices'].items() if supplier in products_suppliers}

Related

Pyomo energy storage system dispatch optimization

I'm trying to create a model optimization for a energy storage system using pyomo. Using the demand in kWh from an household and the electricity prices, I would like to minimize the cost charging and discharging the battery at the right time. I already have a working model for 1 year of data and the model is able to find an optimal solution (see code below). However, when I try find a model for only three months (let's say from October to December), pyomo returns with a termination condition "unbound" but I can't figure out why.
Model for 1 year of data:
#Battery parameters
battery_capacity = 252
total_energy = 13.1
usable_energy = 12.4
nominal_voltage = 51.8
ratio = total_energy/usable_energy
power = usable_energy / ratio
nominal_current = usable_energy / nominal_voltage * 1000
recharging_hours = battery_capacity/nominal_current
battery_level_threshold = 0
model = pyo.ConcreteModel()
#Set time period
model.T = pyo.Set(initialize=pyo.RangeSet(len(df_2021)),ordered=True)
#PARAMETERS:
model.b_efficiency = pyo.Param(initialize=0.9)
model.b_min_cap = pyo.Param(initialize=0)
model.b_max_cap = pyo.Param(initialize=12.4)
model.b_charge_power = pyo.Param(initialize=power)
model.b_discharge_power = pyo.Param(initialize=power)
model.spot_prices = pyo.Param(model.T,initialize=dict(enumerate(df_2021["Price/kWh"],1)),within=pyo.Any)
model.demand = pyo.Param(model.T, initialize=dict(enumerate(df_2021["Demand"],1)),within=pyo.Any)
#Variables : also the variable has to be indexed with the time T
model.b_soc = pyo.Var(model.T, domain = pyo.NonNegativeReals, bounds = (model.b_min_cap, model.b_max_cap))
model.b_discharge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.b_charge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.elect_purchased = pyo.Var(model.T, domain = pyo.NonNegativeReals)
#CONSTRAINTS
#Purchase constraint
def purchase_constraint(model,t):
return model.elect_purchased[t] >= model.demand[t] - model.b_discharge[t] + model.b_charge[t]
#State of charge constraint
def soc_constraint(model,t):
if t == model.T.first():
return model.b_soc[t] == model.b_max_cap / 2 #- model.b_discharge[t] + model.b_charge[t]
else:
return model.b_soc[t] == model.b_soc[t-1] - model.b_discharge[t-1] + model.b_charge[t-1]
#Discharge and charge constraints
def discharge_constraint_1(model,t):
""" Maximum discharge rate within a single hour """
return model.b_discharge[t] <= model.b_discharge_power
def discharge_constraint_2(model,t):<br/>
""" Sets the maximum energy available to be discharged as the SOC - minimum SOC """
return model.b_discharge[t] <= model.b_soc[t] - model.b_min_cap
def charge_constraint_1(model,t):
""" Maximum charge rate within a single hour """
return model.b_charge[t] <= model.b_charge_power
def charge_constraint_2(model,t):<br/>
""" Sets the maximum energy available to be cahrge as the SOC max """
return model.b_charge[t] <= model.b_max_cap - model.b_soc[t]
model.purchase_c = pyo.Constraint(model.T, rule = purchase_constraint)
model.soc_c = pyo.Constraint(model.T, rule = soc_constraint)<br/>
model.discharge_c1 = pyo.Constraint(model.T,rule = discharge_constraint_1)
model.discharge_c2 = pyo.Constraint(model.T,rule = discharge_constraint_2)
model.charge_c1 = pyo.Constraint(model.T,rule = charge_constraint_1)
model.charge_c2 = pyo.Constraint(model.T,rule = charge_constraint_2)
#OBJECTIVE
expr = sum(model.elect_purchased[t] * model.spot_prices[t] for t in model.T)
model.objective = pyo.Objective(rule = expr, sense = pyo.minimize)
opt = pyo.SolverFactory('cbc',executable='/usr/bin/cbc')
results = opt.solve(model)
results.write()
Result 1 year data
The optimal solution is found
However, when I change the dataset, using the SAME model structure and constraints, pyomo doesn't find a solution.
Model for 3 months:
#Battery parameters
battery_capacity = 252
total_energy = 13.1
usable_energy = 12.4
nominal_voltage = 51.8
ratio = total_energy/usable_energy
power = usable_energy / ratio
nominal_current = usable_energy / nominal_voltage * 1000
recharging_hours = battery_capacity/nominal_current
battery_level_threshold = 0
model = pyo.ConcreteModel()
#Set time period
model.T = pyo.Set(initialize=pyo.RangeSet(len(df_2021)),ordered=True)
#PARAMETERS:
model.b_efficiency = pyo.Param(initialize=0.9)
model.b_min_cap = pyo.Param(initialize=0)
model.b_max_cap = pyo.Param(initialize=12.4)
model.b_charge_power = pyo.Param(initialize=power)
model.b_discharge_power = pyo.Param(initialize=power)
model.spot_prices = pyo.Param(model.T,initialize=dict(enumerate(df_2021["Price/kWh"],1)),within=pyo.Any)
model.demand = pyo.Param(model.T, initialize=dict(enumerate(df_2021["Demand"],1)),within=pyo.Any)
#Variables : also the variable has to be indexed with the time T
model.b_soc = pyo.Var(model.T, domain = pyo.NonNegativeReals, bounds = (model.b_min_cap, model.b_max_cap))
model.b_discharge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.b_charge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.elect_purchased = pyo.Var(model.T, domain = pyo.NonNegativeReals)
#CONSTRAINTS
#Purchase constraint
def purchase_constraint(model,t):
return model.elect_purchased[t] >= model.demand[t] - model.b_discharge[t] + model.b_charge[t]
#State of charge constraint
def soc_constraint(model,t):
if t == model.T.first():
return model.b_soc[t] == model.b_max_cap / 2 #- model.b_discharge[t] + model.b_charge[t]
else:
return model.b_soc[t] == model.b_soc[t-1] - model.b_discharge[t-1] + model.b_charge[t-1]
#Discharge and charge constraints
def discharge_constraint_1(model,t):
""" Maximum discharge rate within a single hour """
return model.b_discharge[t] <= model.b_discharge_power
def discharge_constraint_2(model,t):<br/>
""" Sets the maximum energy available to be discharged as the SOC - minimum SOC """
return model.b_discharge[t] <= model.b_soc[t] - model.b_min_cap
def charge_constraint_1(model,t):
""" Maximum charge rate within a single hour """
return model.b_charge[t] <= model.b_charge_power
def charge_constraint_2(model,t):<br/>
""" Sets the maximum energy available to be cahrge as the SOC max """
return model.b_charge[t] <= model.b_max_cap - model.b_soc[t]
model.purchase_c = pyo.Constraint(model.T, rule = purchase_constraint)
model.soc_c = pyo.Constraint(model.T, rule = soc_constraint)<br/>
model.discharge_c1 = pyo.Constraint(model.T,rule = discharge_constraint_1)
model.discharge_c2 = pyo.Constraint(model.T,rule = discharge_constraint_2)
model.charge_c1 = pyo.Constraint(model.T,rule = charge_constraint_1)
model.charge_c2 = pyo.Constraint(model.T,rule = charge_constraint_2)
#OBJECTIVE
expr = sum(model.elect_purchased[t] * model.spot_prices[t] for t in model.T)
model.objective = pyo.Objective(rule = expr, sense = pyo.minimize)
opt = pyo.SolverFactory('cbc',executable='/usr/bin/cbc')
results = opt.solv
Pyomo returns:
WARNING: Loading a SolverResults object with a warning status into
model.name="unknown";
- termination condition: unbounded
- message from solver: <undefined>
# ==========================================================
# = Solver Results =
# ==========================================================
# ----------------------------------------------------------
# Problem Information
# ----------------------------------------------------------
Problem:
- Name: unknown
Lower bound: None
Upper bound: inf
Number of objectives: 1
Number of constraints: 16993
Number of variables: 11329
Number of nonzeros: 2832
Sense: minimize
# ----------------------------------------------------------
# Solver Information
# ----------------------------------------------------------
Solver:
- Status: warning
User time: -1.0
System time: 0.45
Wallclock time: 0.58
Termination condition: unbounded
Termination message: Model was proven to be unbounded.
Statistics:
Branch and bound:
Number of bounded subproblems: 0
Number of created subproblems: 0
Black box:
Number of iterations: 0
Error rc: 0
Time: 0.6151924133300781
Since the only change between the two runs is the length of the dataframe, I don't really know where to look for the error.
Given that the model works on some data, but not on an alternate data source, we can obviously focus a bit on the data set (which isn't shown).
We have a huge clue in the error report that the problem is unbounded. This means that there is nothing to prevent the objective function from running away to infinity, or negative infinity in the case of a minimization problem. So, let's look at your objective function. You are:
sum(demand[t] * price[t] for t in T)
The domain of your demand variable is set to non-negative real numbers (good) and price[t] is a parameter read in from data, and you are minimizing. So the only way this could run away to negative infinity is if there are one (or more) negative prices in your data, which the solver would then act on and demand would be unbounded.
So, comb your data set. if you are using pandas, just use a logical search for rows where price is < 0, you'll likely find at least one. Then you'll have to decide if that is a typo or if it is realistic to have a negative price (which could happen in some systems), and if it is legit, you will have to impose some other constraint to limit the model in that situation.

Replace dot product for loop Numpy

I am trying to replace the dot product for loop using something faster like NumPy
I did research on dot product and kind of understand and can get it working with toy data in a few ways in but not 100% when it comes to implementing it for actual use with a data frame.
I looked at these and other SO threads to no luck avoide loop dot product, matlab and dot product subarrays without for loop and multiple numpy dot products without a loop
looking to do something like this which works with toy numbers in np array
u1 =np.array([1,2,3])
u2 =np.array([2,3,4])
v1.dot(v2)
20
u1 =np.array([1,2,3])
u2 =np.array([2,3,4])
(u1 * u2).sum()
20
u1 =np.array([1,2,3])
u2 =np.array([2,3,4])
sum([x1*x2 for x1, x2 in zip (u1, u2)])
20
this is the current working get dot product
I would like to do this with out the for loop
def get_dot_product(self, courseid1, courseid2, unit_vectors):
u1 = unit_vectors[courseid1]
u2 = unit_vectors[courseid2]
dot_product = 0.0
for dimension in u1:
if dimension in u2:
dot_product += u1[dimension] * u2[dimension]
return dot_product
** code**
#!/usr/bin/env python
# coding: utf-8
class SearchRecommendationSystem:
def __init__(self):
pass
def get_bag_of_words(self, titles_lines):
bag_of_words = {}
for index, row in titles_lines.iterrows():
courseid, course_bag_of_words = self.get_course_bag_of_words(row)
for word in course_bag_of_words:
word = str(word).strip() # added
if word not in bag_of_words:
bag_of_words[word] = course_bag_of_words[word]
else:
bag_of_words[word] += course_bag_of_words[word]
return bag_of_words
def get_course_bag_of_words(self, line):
course_bag_of_words = {}
courseid = line['courseid']
title = line['title'].lower()
description = line['description'].lower()
wordlist = title.split() + description.split()
if len(wordlist) >= 10:
for word in wordlist:
word = str(word).strip() # added
if word not in course_bag_of_words:
course_bag_of_words[word] = 1
else:
course_bag_of_words[word] += 1
return courseid, course_bag_of_words
def get_sorted_results(self, d):
kv_list = d.items()
vk_list = []
for kv in kv_list:
k, v = kv
vk = v, k
vk_list.append(vk)
vk_list.sort()
vk_list.reverse()
k_list = []
for vk in vk_list[:10]:
v, k = vk
k_list.append(k)
return k_list
def get_keywords(self, titles_lines, bag_of_words):
n = sum(bag_of_words.values())
keywords = {}
for index, row in titles_lines.iterrows():
courseid, course_bag_of_words = self.get_course_bag_of_words(row)
term_importance = {}
for word in course_bag_of_words:
word = str(word).strip() # extra
tf_course = (float(course_bag_of_words[word]) / sum(course_bag_of_words.values()))
tf_overall = float(bag_of_words[word]) / n
term_importance[word] = tf_course / tf_overall
keywords[str(courseid)] = self.get_sorted_results(term_importance)
return keywords
def get_inverted_index(self, keywords):
inverted_index = {}
for courseid in keywords:
for keyword in keywords[courseid]:
if keyword not in inverted_index:
keyword = str(keyword).strip() # added
inverted_index[keyword] = []
inverted_index[keyword].append(courseid)
return inverted_index
def get_search_results(self, query_terms, keywords, inverted_index):
search_results = {}
for term in query_terms:
term = str(term).strip()
if term in inverted_index:
for courseid in inverted_index[term]:
if courseid not in search_results:
search_results[courseid] = 0.0
search_results[courseid] += (
1 / float(keywords[courseid].index(term) + 1) *
1 / float(query_terms.index(term) + 1)
)
sorted_results = self.get_sorted_results(search_results)
return sorted_results
def get_titles(self, titles_lines):
titles = {}
for index, row in titles_lines.iterrows():
titles[row['courseid']] = row['title'][:60]
return titles
def get_unit_vectors(self, keywords, categories_lines):
norm = 1.884
cat = {}
subcat = {}
for line in categories_lines[1:]:
courseid_, category, subcategory = line.split('\t')
cat[courseid_] = category.strip()
subcat[courseid_] = subcategory.strip()
unit_vectors = {}
for courseid in keywords:
u = {}
if courseid in cat:
u[cat[courseid]] = 1 / norm
u[subcat[courseid]] = 1 / norm
for keyword in keywords[courseid]:
u[keyword] = (1 / float(keywords[courseid].index(keyword) + 1) / norm)
unit_vectors[courseid] = u
return unit_vectors
def get_dot_product(self, courseid1, courseid2, unit_vectors):
u1 = unit_vectors[courseid1]
u2 = unit_vectors[courseid2]
dot_product = 0.0
for dimension in u1:
if dimension in u2:
dot_product += u1[dimension] * u2[dimension]
return dot_product
def get_recommendation_results(self, seed_courseid, keywords, inverted_index, unit_vectors):
courseids = []
seed_courseid = str(seed_courseid).strip()
for keyword in keywords[seed_courseid]:
for courseid in inverted_index[keyword]:
if courseid not in courseids and courseid != seed_courseid:
courseids.append(courseid)
dot_products = {}
for courseid in courseids:
dot_products[courseid] = self.get_dot_product(seed_courseid, courseid, unit_vectors)
sorted_results = self.get_sorted_results(dot_products)
return sorted_results
def Final(self):
print("Reading Title file.......")
titles_lines = open('s2-titles.txt', encoding="utf8").readlines()
print("Reading Category file.......")
categories_lines = open('s2-categories.tsv', encoding = "utf8").readlines()
print("Getting Supported Functions Data")
bag_of_words = self.get_bag_of_words(titles_lines)
keywords = self.get_keywords(titles_lines, bag_of_words)
inverted_index = self.get_inverted_index(keywords)
titles = self.get_titles(titles_lines)
print("Getting Unit Vectors")
unit_vectors = self.get_unit_vectors(keywords=keywords, categories_lines=categories_lines)
#Search Part
print("\n ############# Started Search Query System ############# \n")
query = input('Input your search query: ')
while query != '':
query_terms = query.split()
search_sorted_results = self.get_search_results(query_terms, keywords, inverted_index)
print(f"==> search results for query: {query.split()}")
for search_result in search_sorted_results:
print(f"{search_result.strip()} - {str(titles[search_result]).strip()}")
#ask again for query or quit the while loop if no query is given
query = input('Input your search query [hit return to finish]: ')
print("\n ############# Started Recommendation Algorithm System ############# \n")
# Recommendation ALgorithm Part
seed_courseid = (input('Input your seed courseid: '))
while seed_courseid != '':
seed_courseid = str(seed_courseid).strip()
recom_sorted_results = self.get_recommendation_results(seed_courseid, keywords, inverted_index, unit_vectors)
print('==> recommendation results:')
for rec_result in recom_sorted_results:
print(f"{rec_result.strip()} - {str(titles[rec_result]).strip()}")
get_dot_product_ = self.get_dot_product(seed_courseid, str(rec_result).strip(), unit_vectors)
print(f"Dot Product Value: {get_dot_product_}")
seed_courseid = (input('Input seed courseid [hit return to finish]:'))
if __name__ == '__main__':
obj = SearchRecommendationSystem()
obj.Final()
s2-categories.tsv
courseid category subcategory
21526 Design 3D & Animation
153082 Marketing Advertising
225436 Marketing Affiliate Marketing
19482 Office Productivity Apple
33883 Office Productivity Apple
59526 IT & Software Operating Systems
29219 Personal Development Career Development
35057 Personal Development Career Development
40751 Personal Development Career Development
65210 Personal Development Career Development
234414 Personal Development Career Development
Example of how s2-titles.txt looks
courseidXXXYYYZZZtitleXXXYYYZZZdescription
3586XXXYYYZZZLearning Tools for Mrs B's Science Classes This is a series of lessons that will introduce students to the learning tools that will be utilized throughout the schoXXXYYYZZZThis is a series of lessons that will introduce students to the learning tools that will be utilized throughout the school year The use of these tools serves multiple purposes 1 Allow the teacher to give immediate and meaningful feedback on work that is in progress 2 Allow students to have access to content and materials when outside the classroom 3 Provide a variety of methods for students to experience learning materials 4 Provide a variety of methods for students to demonstrate learning 5 Allow for more time sensitive correction grading and reflections on concepts that are assessed
Evidently unit_vectors is a dictionary, from which you extract to 2 values, u1 and u2.
But what are those? Evidently dicts as well (this iteration would not make sense with a list):
for dimension in u1:
if dimension in u2:
dot_product += u1[dimension] * u2[dimension]
But what is u1[dimension]? A list? An array.
Normally dict are access by key as you do here. There isn't a numpy style "vectorization". vals = list(u1.values()) gets a lists of all values, and conceivably that could be made into an array (if the elements are right)
arr1 = np.array(list(u1.values()))
and a np.dot(arr1, arr2) might work
You'll get the best answers if you give small concrete examples - with real working data (and skip the complex generating code). Focus on the core of the problem, so we can grasp the issue with a 30 second read!
===
Looking more in depth at your dot function; this replicates the core (I think). Initially I missed the fact that you aren't iterating on u2 keys, but rather seeking matching ones.
def foo(dd):
x = 0
u1 = dd['u1']
u2 = dd['u2']
for k in u1:
if k in u2:
x += u1[k]*u2[k]
return x
Then making a dictionary of dictionaries:
In [30]: keys=list('abcde'); values=[1,2,3,4,5]
In [31]: adict = {k:v for k,v in zip(keys,values)}
In [32]: dd = {'u1':adict, 'u2':adict}
In [41]: dd
Out[41]:
{'u1': {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5},
'u2': {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}}
In [42]: foo(dd)
Out[42]: 55
In this case the subdictionaries match, so we get the same value with a simple array dot:
In [43]: np.dot(values,values)
Out[43]: 55
But if u2 was different, with different key/value pairs, and possibly different keys the result will be different. I don't see a way around the iterative access by keys. The sum-of-products part of the job is minor compared to the dictionary access.
In [44]: dd['u2'] = {'e':3, 'f':4, 'a':3}
In [45]: foo(dd)
Out[45]: 18
We could construct other data structures that are more suitable to a fast dot like calculation. But that's another topic.
Modified method
def get_dot_product(self, courseid1, courseid2, unit_vectors):
# u1 = unit_vectors[courseid1]
# u2 = unit_vectors[courseid2]
# dimensions = set(u1).intersection(set(u2))
# dot_product = sum(u1[dimension] * u2.get(dimension, 0) for dimension in dimensions)
u1 = unit_vectors[courseid1]
u2 = unit_vectors[courseid2]
dot_product = sum(u1[dimension] * u2.get(dimension, 0) for dimension in u2)
return dot_product

How to loop the following code in Python?

I am new to Python but I tried to create the following trading strategy but cannot find a way to loop it for different products (trading hours in this case).
def strategy(area_code, product, orders, environment):
order = None
new_orders = []
Quantity = 5
price_delta = 0.1
def process_flex(Plant):
order = None
Tur1 = Plant + "1"
if Tur1Volume > 0:
if Tur1Price:
order = None
if check_if_we_have_order_on_the_market(Plant,Tur1)==0:
order = package.create_sell_order(area_code, Tur1Volume, Quantity, calculate_selling_price(Tur1Price), price_delta, product, environment.current_datetime).with_label((Plant,Tur1))
if order:
new_orders.append(order)
else:
order = None
return
process_flex("bla")
process_flex("blabla")
process_flex("blablabla")
return new_orders
This code is only working for one product (1 hour) and does not loop for all 24 products.
I thought that it could work like this:
for product in products:
Plant = ['bla', 'blabla', 'blablabla']
for i in Plant:
order = process_flex(Plant)
return_orders.append(order)
return return_orders
Unfortunately, it did not work. Do you have any idea on the solution?
Thank a lot in advance!
You want to swap Plant to :
order = process_flex(i)
because i is an element of Plant
for product in products:
Plant = ['bla', 'blabla', 'blablabla']
for i in Plant:
order = process_flex(i)
return_orders.append(order)
return return_orders

Odoo 8 Allow Negative Invoice Line Item

I have a product called "Coupon" with negative amount which is used to offset the product price. However, it seems like Odoo 8 does not allow computation of negative amount to price_subtotal (it becomes 0.00):
Coupon ... ... 1 Each -40.0000 0.0000
When I remove the negative sign, it computes
Coupon ... ... 1 Each 40.0000 40.0000
From an accounting perspective, the total invoice should not be negative. That stays true. However, I do need to allow negative computation of invoice line item(s). Where and what do I need to change? I tried looking into account/account.py but to no avail so far - it's all just "tax" related.
Thanks in advance!
Details of the amount column for the line total
class account_invoice(models.Model)
....
#api.one
#api.depends('invoice_line.price_subtotal', 'tax_line.amount')
def _compute_amount(self):
self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line)
self.amount_tax = sum(line.amount for line in self.tax_line)
self.amount_total = self.amount_untaxed + self.amount_tax
....
class account_invoice_line(models.Model):
_name = "account.invoice.line"
_description = "Invoice Line"
_order = "invoice_id,sequence,id"
#api.one
#api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity',
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id')
def _compute_price(self):
price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
taxes = self.invoice_line_tax_id.compute_all(price, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
self.price_subtotal = taxes['total']
if self.invoice_id:
self.price_subtotal = self.invoice_id.currency_id.round(self.price_subtotal)
#api.model
def _default_price_unit(self):
if not self._context.get('check_total'):
return 0
total = self._context['check_total']
for l in self._context.get('invoice_line', []):
if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
vals = l[2]
price = vals.get('price_unit', 0) * (1 - vals.get('discount', 0) / 100.0)
total = total - (price * vals.get('quantity'))
taxes = vals.get('invoice_line_tax_id')
if taxes and len(taxes[0]) >= 3 and taxes[0][2]:
taxes = self.env['account.tax'].browse(taxes[0][2])
tax_res = taxes.compute_all(price, vals.get('quantity'),
product=vals.get('product_id'), partner=self._context.get('partner_id'))
for tax in tax_res['taxes']:
total = total - tax['amount']
return total
Odoo's default behaviour is handling it as expected. The problem is custom code. (For more information read the questions comments)

Calculate the future value for only one category using the IRR (Python)

import xlrd
import numpy
fileWorkspace = 'C://Users/jod/Desktop/'
wb1 = xlrd.open_workbook(fileWorkspace + 'assign2.xls')
sh1 = wb1.sheet_by_index(0)
time,amount,category = [],[],[]
for a in range(2,sh1.nrows):
time.append(int(sh1.cell(a,0).value)) # Pulling time from excel (column A)
amount.append(float(sh1.cell(a,1).value)) # Pulling amount from excel (column B)
category.append(str(sh1.cell(a,2).value)) # Pulling category from excel (column C)
#print(time)
#print(amount)
#print(category)
print('\n')
p_p2 = str(sh1.cell(0,1))
p_p1 = p_p2.replace("text:'","")
pp = p_p1.replace("'","")
print(pp) # Printing the type of pay period (Row 1, col B)
c_p2 = str(sh1.cell(1,1))
c_p1 = c_p2.replace("text:'","")
cp = c_p1.replace("'","")
print(cp) # Printing the type of compound period (Row 2, col B)
netflow = 0
outflow = 0
inflow = 0
flow = 0
cat = ["Sales", "Salvage", "Subsidy", "Redeemable", "Utility", "Labor",
"Testing", "Marketing", "Materials", "Logistics"]
if pp == "Years" and cp == "Years": # if pay period and compound period are both in years
IRR = numpy.irr(amount) * 100 # Calculates the internal rate of return (IRR)
print ("IRR:", round(IRR, 2), '%', '\n') # prints (IRR)
for i in time: # for every value in time array
if cat[5] in category: # if "Labor" for cat array is in category array or not
# calculates the present values using all the amount values (col B) instead of
# just using the ones that has "Labor" category label beside them
# Need to make every other value 0, such as beside "Redeemable" and "Salvage"
flow = amount[i] / numpy.power((1 + (IRR/100)), time[i])
if flow>0:
inflow = inflow + flow
if flow<0:
outflow = outflow + flow
print ('Present Value (P) is:', round(flow,0), '\n')
netflow = outflow + inflow
print("In year 0 or current year")
print("-------")
print ('Outflow is: ', round(outflow,0))
print ('Inflow is: ', round(inflow,0))
print ('Netflow is: ', round(netflow,0), '\n')
outflow2 = (round(outflow,0))*(1+(IRR/100))**(9)
inflow2 = (round(inflow,0))*(1+(IRR/100))**(9)
netflow2 = outflow2 + inflow2
print("In year 9")
print("-------")
print ('Outflow is: ', round(outflow2,0))
print ('Inflow is: ', round(inflow2,0))
print ('Netflow is: ', round(netflow2,0), '\n')
I have commented important lines of code for clarification.
Here is the original question:
illustrate the breakdown of major project revenues and expenses by category as a percentage of that project’s future value in year 9. The illustration must also clearly indicate the total future value of the project in year 9 as well as the IRR.
There will be a total of 10 revenue and cost categories that a project may be composed of. The categories are: Sales, salvage, subsidy, redeemable, utility, labor, testing, marketing, materials and logistics. All revenues and expenses will fall in one of these ten categories. The project pay period and compound period will be identified at the top of the Excel sheet. Pay period and compound period may be designated as any of the following: years, quarters, months.
I am getting confused because I am not able to pull the only values from beside the "Labor", "Redeemable", or "Salvage". I just don't know where I am making a mistake, or there is something that is incomplete. Below is the excel file image:
Excel File Image 2
Excel File Image 3
After revising, all cashflows are discounted at the irr. What is done is the following:
i) determineAdjustments takes the pay period (column A) and adjusts if for the year ended (if it is a monthly amount it puts it in the proper year ended) and if its monthly puts in in the month ended (no adjustment necessary). This will divide the pay period by 12 if yearly cash flows are needed (yearly compounding)
ii) IRR is calculated, and the compounding period is used to adjust the monthly IRR for monthly pay periods
iii) all expenses are discounted at the IRR and input into a list for cat_contributions['category_name'] = [discounted period 1, discounted period 2 ... ]
iv) Then the net inflows and outflows are sums of these.
I can't type up data in the spreadsheets from the images as that would take a while, but maybe tinker with this and see if you can get it to work.
from __future__ import division
import xlrd
import numpy
import os
import math
def main(xls = 'xls_name.xlsx', sh = 0):
#save script in same folder as the xls file
os.chdir( os.getcwd() )
wb = xlrd.open_workbook(xls)
sh = wb.sheet_by_index(0)
pay_period = sh.cell_value(0,1)
compounding_period = sh.cell_value(1,1)
compounding_factor, pay_factor = determineAdjustments(
pay_period, compounding_period)
number_of_periods = max( sh.col_values(0, start_rowx = 2) )
flow_per_period = [ 0*i for i in range( int( math.ceil( number_of_periods/pay_factor ) ) + 1 ) ]#list of length number of pay_periods
for r in range(2,sh.nrows):
pay_period = int( math.ceil( sh.cell_value(r,0) / pay_factor ) )
flow_per_period[pay_period] += sh.cell_value(r,1) #unadjusted cash flows
irr = calculateIRR(flow_per_period, compounding_factor)
cat_contributions = sortExpenditures(sh, irr, pay_factor)
total_cat_contributions, netflow, total_outflow, total_inflow = calculateFlows(cat_contributions)
printStats(cat_contributions, irr, compounding_factor, pay_factor,
total_cat_contributions, netflow, total_outflow, total_inflow)
return
def determineAdjustments(pay_period, compounding_period):
if compounding_period == 'years':
compounding_factor = 1
if pay_period == 'months':
pay_factor = 12
if pay_period == 'years':
pay_factor = 1
#assume no days pay periods
if compounding_period == 'months':
compounding_factor = 12
#assume no yearly payouts and that the
#all payments are in months
pay_factor = 1
return compounding_factor, pay_factor
def calculateIRR(cashflow, compounding_factor):
irr = numpy.irr(cashflow)
irr_comp = (1 + irr)**compounding_factor - 1
#seems like in first example it uses rounded irr, can do something like:
#irr_comp = round(irr_comp,4)
return irr_comp
def sortExpenditures(sh, irr, pay_factor):
#percentages and discounting occurs at the IRR caculated in the main
#function
cat = ["Sales", "Salvage", "Subsidy", "Redeemable", "Utility", "Labor",
"Testing", "Marketing", "Materials", "Logistics"]
#python dictionary to sort contributions into categories
cat_contributions = {}
for c in cat:
cat_contributions[c] = []
# create list of contributions of each list item to FV in a dictionary
for r in range(2,sh.nrows):
try:
#discounted cash flow of each expenditure
#using formula FV = expenditure/(1+i)^n
cat_contributions[sh.cell_value(r,2)].append(
sh.cell_value(r,1) / ( (1 + irr) ** (sh.cell_value(r,0)/pay_factor) )
)
except KeyError:
print "No category for type: " + sh.cell_value(r,2) +'\n'
return cat_contributions
def calculateFlows(cat_contributions):
total_outflow = 0
total_inflow = 0
total_cat_contributions = {}
for cat in cat_contributions:
total_cat_contributions[cat] = sum( cat_contributions[cat] )
if total_cat_contributions[cat] < 0:
total_outflow += total_cat_contributions[cat]
else:
total_inflow += total_cat_contributions[cat]
netflow = total_inflow + total_outflow
return total_cat_contributions, netflow, total_outflow, total_inflow
def printStats(cat_contributions, irr, compounding_factor, pay_period,
total_cat_contributions, netflow, total_outflow, total_inflow):
print "IRR: "+str(irr*100) +' %'
if compounding_factor == 1: print "Compounding: Yearly"
if compounding_factor == 12: print "Compounding: Monthly"
if pay_period == 1: "Cashflows: Year Ended"
if pay_period == 12: "Cashflows: Month Ended"
print "Future Value (Net Adjusted Cashflow): " +str(netflow)
print "Adjusted Inflows: " + str(total_inflow)
print "Adjusted Outflows: " + str(total_outflow) +'\n'
for cat in total_cat_contributions:
if total_cat_contributions[cat] != 0:
print '-----------------------------------------------------'
print cat + '\n'
print "Total Contribution to FV " + str( total_cat_contributions[cat] )
if total_cat_contributions[cat] < 0:
print "Contribution to Expenses: " + str ( abs(100 * total_cat_contributions[cat]/total_outflow) )
else:
print "Contribution to Revenues: " + str ( abs(100 * total_cat_contributions[cat]/total_inflow) ) +'\n'
main(xls='Book1.xlsx')

Categories