pub fn init() -> PyResult<()>Examples found in repository?
More examples
examples/product-mix.rs (line 13)
8fn main() {
9 // Start timing
10 let start_time = Instant::now();
11
12 // Initialize engine (as per template)
13 engine::init();
14
15 println!("Balance Engine - Production Mix Optimization Demo");
16
17 // Define the problem data
18 let raw_materials = vec!["A", "B", "C"];
19 let products = vec!["Super", "Unleaded", "Super_Unleaded"];
20
21 // Raw material properties
22 let mut octane_number = HashMap::new();
23 octane_number.insert("A", 120.0);
24 octane_number.insert("B", 90.0);
25 octane_number.insert("C", 130.0);
26
27 let mut material_cost = HashMap::new();
28 material_cost.insert("A", 38.0);
29 material_cost.insert("B", 42.0);
30 material_cost.insert("C", 105.0);
31
32 let mut max_available = HashMap::new();
33 max_available.insert("A", 1000.0);
34 max_available.insert("B", 1200.0);
35 max_available.insert("C", 700.0);
36
37 // Product properties
38 let mut octane_requirement = HashMap::new();
39 octane_requirement.insert("Super", 94.0);
40 octane_requirement.insert("Unleaded", 92.0);
41 octane_requirement.insert("Super_Unleaded", 96.0);
42
43 let mut selling_price = HashMap::new();
44 selling_price.insert("Super", 85.0);
45 selling_price.insert("Unleaded", 80.0);
46 selling_price.insert("Super_Unleaded", 88.0);
47
48 let mut demand = HashMap::new();
49 demand.insert("Super", 800.0);
50 demand.insert("Unleaded", 1100.0);
51 demand.insert("Super_Unleaded", 500.0);
52
53 // Create variables for the problem
54 let mut vars = variables!();
55
56 // z[i,j] = amount of raw material i used in product j (tons)
57 let mut z = HashMap::new();
58 for &i in &raw_materials {
59 for &j in &products {
60 z.insert((i, j), vars.add(variable().min(0.0).name(format!("z_{}_{}", i, j))));
61 }
62 }
63
64 // y[j] = total amount of product j produced (tons)
65 let mut y = HashMap::new();
66 for &j in &products {
67 y.insert(j, vars.add(variable().min(0.0).max(*demand.get(j).unwrap()).name(format!("y_{}", j))));
68 }
69
70 // Objective function: maximize total profit
71 let mut objective = Expression::from(0.0);
72
73 // Revenue part
74 for &j in &products {
75 objective += *selling_price.get(j).unwrap() * *y.get(j).unwrap();
76 }
77
78 // Material costs part
79 for &i in &raw_materials {
80 for &j in &products {
81 objective -= *material_cost.get(i).unwrap() * *z.get(&(i, j)).unwrap();
82 }
83 }
84
85 // Create model with maximization objective
86 let mut model = vars.maximise(objective.clone()).using(default_solver);
87
88 // Add constraints
89
90 // 1. Raw material availability constraints
91 for &i in &raw_materials {
92 let mut usage = Expression::from(0.0);
93 for &j in &products {
94 usage += z.get(&(i, j)).unwrap();
95 }
96 model = model.with(constraint!(usage <= *max_available.get(i).unwrap()));
97 }
98
99 // 2. Mass balance constraints (sum of raw materials = product quantity)
100 for &j in &products {
101 let mut sum_materials = Expression::from(0.0);
102 for &i in &raw_materials {
103 sum_materials += z.get(&(i, j)).unwrap();
104 }
105 model = model.with(constraint!(sum_materials == y.get(j).unwrap()));
106 }
107
108 // 3. Octane requirements
109 for &j in &products {
110 let mut weighted_octane = Expression::from(0.0);
111 for &i in &raw_materials {
112 weighted_octane += *octane_number.get(i).unwrap() * *z.get(&(i, j)).unwrap();
113 }
114 let octane_req = *octane_requirement.get(j).unwrap() * *y.get(j).unwrap();
115 model = model.with(constraint!(weighted_octane >= octane_req));
116 }
117
118 // Solve the model
119 match model.solve() {
120 Ok(solution) => {
121 println!("\nOptimal Solution Found:");
122 println!("Total Profit: €{:.2}", solution.eval(&objective));
123
124 // Print production plan
125 println!("\nProduction Quantities:");
126 for &j in &products {
127 let amount = solution.value(*y.get(j).unwrap());
128 println!("{}: {:.2} tons (Max demand: {} tons)", j, amount, demand.get(j).unwrap());
129 }
130
131 // Print raw material usage
132 println!("\nRaw Material Usage:");
133 for &i in &raw_materials {
134 let mut total_used = 0.0;
135 for &j in &products {
136 total_used += solution.value(*z.get(&(i, j)).unwrap());
137 }
138 let utilization = total_used / max_available.get(i).unwrap() * 100.0;
139 println!("{}: {:.2} tons ({:.1}% of available {} tons)",
140 i, total_used, utilization, max_available.get(i).unwrap());
141 }
142
143 // Print product composition for each product
144 println!("\nProduct Composition:");
145 for &j in &products {
146 println!("\n{} Mix:", j);
147 let total_product = solution.value(*y.get(j).unwrap());
148 if total_product > 0.001 { // Only print if product is produced
149 for &i in &raw_materials {
150 let tons_used = solution.value(*z.get(&(i, j)).unwrap());
151 let percentage = (tons_used / total_product) * 100.0;
152 println!(" {}: {:.1}% ({:.2} tons)", i, percentage, tons_used);
153 }
154 } else {
155 println!(" Not produced");
156 }
157 }
158
159 // Calculate weighted average octane for each product
160 println!("\nOctane Verification:");
161 for &j in &products {
162 let total_product = solution.value(*y.get(j).unwrap());
163 if total_product > 0.001 { // Only print if product is produced
164 let mut weighted_octane_sum = 0.0;
165 for &i in &raw_materials {
166 weighted_octane_sum += octane_number.get(i).unwrap() * solution.value(*z.get(&(i, j)).unwrap());
167 }
168 let achieved_octane = weighted_octane_sum / total_product;
169 println!("{}: Requirement = {}, Achieved = {:.2}",
170 j, octane_requirement.get(j).unwrap(), achieved_octane);
171 }
172 }
173
174 },
175 Err(e) => {
176 println!("No optimal solution found. Error: {:?}", e);
177 }
178 }
179
180 // Print elapsed time
181 let elapsed = start_time.elapsed();
182 println!("\nTime elapsed: {:?}", elapsed);
183}examples/multi-period.rs (line 13)
8fn main() {
9 // Start timing
10 let start_time = Instant::now();
11
12 // Initialize engine (as per template)
13 engine::init();
14
15 println!("Balance Engine - Multi-Period Production Planning Demo");
16
17 // Define the problem data
18 let products = vec!["A", "B"];
19 let periods = vec!["January", "February", "March"];
20
21 // Initial conditions
22 let mut initial_inventory = HashMap::new();
23 initial_inventory.insert("A", 100.0);
24 initial_inventory.insert("B", 120.0);
25
26 let mut safety_stock = HashMap::new();
27 safety_stock.insert("A", 130.0);
28 safety_stock.insert("B", 110.0);
29
30 let mut production_cost = HashMap::new();
31 production_cost.insert("A", 20.0);
32 production_cost.insert("B", 25.0);
33
34 let holding_cost_rate = 0.02; // 2% of production cost
35
36 // Calculate holding cost per unit per period
37 let mut holding_cost = HashMap::new();
38 for &p in &products {
39 holding_cost.insert(p, production_cost[&p] * holding_cost_rate);
40 }
41
42 // Demand forecasts
43 let mut demand = HashMap::new();
44 demand.insert(("A", "January"), 700.0);
45 demand.insert(("A", "February"), 900.0);
46 demand.insert(("A", "March"), 1000.0);
47 demand.insert(("B", "January"), 800.0);
48 demand.insert(("B", "February"), 600.0);
49 demand.insert(("B", "March"), 900.0);
50
51 // Resource capacities
52 let mut machine_capacity = HashMap::new();
53 machine_capacity.insert("January", 3000.0);
54 machine_capacity.insert("February", 2800.0);
55 machine_capacity.insert("March", 3600.0);
56
57 let mut labor_capacity = HashMap::new();
58 labor_capacity.insert("January", 2500.0);
59 labor_capacity.insert("February", 2300.0);
60 labor_capacity.insert("March", 2400.0);
61
62 // Resource requirements per unit
63 let mut machine_hours = HashMap::new();
64 machine_hours.insert("A", 1.5);
65 machine_hours.insert("B", 1.6);
66
67 let mut labor_hours = HashMap::new();
68 labor_hours.insert("A", 1.1);
69 labor_hours.insert("B", 1.2);
70
71 // Create variables for the problem
72 let mut vars = variables!();
73
74 // x[p,t] = production of product p in period t
75 let mut x = HashMap::new();
76 for &p in &products {
77 for &t in &periods {
78 x.insert((p, t), vars.add(variable().min(0.0).integer().name(format!("x_{}_{}", p, t))));
79 }
80 }
81
82 // inv[p,t] = inventory of product p at the end of period t
83 let mut inv = HashMap::new();
84 for &p in &products {
85 for &t in &periods {
86 inv.insert((p, t), vars.add(variable().min(0.0).integer().name(format!("inv_{}_{}", p, t))));
87 }
88 }
89
90 // Objective function: minimize total cost (production + inventory holding)
91 let mut objective = Expression::from(0.0);
92
93 // Production costs
94 for &p in &products {
95 for &t in &periods {
96 objective += production_cost[&p] * *x.get(&(p, t)).unwrap();
97 }
98 }
99
100 // Holding costs
101 for &p in &products {
102 for &t in &periods {
103 objective += holding_cost[&p] * *inv.get(&(p, t)).unwrap();
104 }
105 }
106
107 // Create model with minimization objective
108 let mut model = vars.minimise(objective.clone()).using(default_solver);
109
110 // Add constraints
111
112 // 1. Inventory balance constraints
113 for &p in &products {
114 for (i, &t) in periods.iter().enumerate() {
115 if i == 0 { // first period
116 model = model.with(constraint!(
117 *inv.get(&(p, t)).unwrap() == initial_inventory[&p] + *x.get(&(p, t)).unwrap() - demand[&(p, t)]
118 ));
119 } else {
120 let prev_t = periods[i - 1];
121 model = model.with(constraint!(
122 *inv.get(&(p, t)).unwrap() == *inv.get(&(p, prev_t)).unwrap() + *x.get(&(p, t)).unwrap() - demand[&(p, t)]
123 ));
124 }
125 }
126 }
127
128 // 2. Capacity constraints
129 for &t in &periods {
130 // Machine capacity
131 let mut machine_usage = Expression::from(0.0);
132 for &p in &products {
133 machine_usage += machine_hours[&p] * *x.get(&(p, t)).unwrap();
134 }
135 model = model.with(constraint!(machine_usage <= machine_capacity[&t]));
136
137 // Labor capacity
138 let mut labor_usage = Expression::from(0.0);
139 for &p in &products {
140 labor_usage += labor_hours[&p] * *x.get(&(p, t)).unwrap();
141 }
142 model = model.with(constraint!(labor_usage <= labor_capacity[&t]));
143 }
144
145 // 3. Safety stock requirements (end of planning horizon)
146 for &p in &products {
147 let last_period = periods.last().unwrap();
148 model = model.with(constraint!(*inv.get(&(p, last_period)).unwrap() >= safety_stock[&p]));
149 }
150
151 // Solve the model
152 match model.solve() {
153 Ok(solution) => {
154 println!("\nOptimal Solution Found:");
155 println!("Total Cost: €{:.2}", solution.eval(&objective));
156
157 // Print production plan
158 println!("\nProduction Plan:");
159 println!("{:<10} {:<10} {:<10}", "Period", "Product A", "Product B");
160 println!("{}", "-".repeat(30));
161 for &t in &periods {
162 println!("{:<10} {:<10} {:<10}",
163 t,
164 solution.value(*x.get(&("A", t)).unwrap()) as i32,
165 solution.value(*x.get(&("B", t)).unwrap()) as i32);
166 }
167
168 // Print ending inventory
169 println!("\nEnding Inventory:");
170 println!("{:<10} {:<10} {:<10}", "Period", "Product A", "Product B");
171 println!("{}", "-".repeat(30));
172 for &t in &periods {
173 println!("{:<10} {:<10} {:<10}",
174 t,
175 solution.value(*inv.get(&("A", t)).unwrap()) as i32,
176 solution.value(*inv.get(&("B", t)).unwrap()) as i32);
177 }
178
179 // Calculate resource utilization
180 println!("\nResource Utilization:");
181 println!("{:<10} {:<30} {:<30}",
182 "Period",
183 "Machine Hours (Used/Available)",
184 "Labor Hours (Used/Available)");
185 println!("{}", "-".repeat(70));
186 for &t in &periods {
187 let mut machine_used = 0.0;
188 let mut labor_used = 0.0;
189
190 for &p in &products {
191 machine_used += machine_hours[&p] * solution.value(*x.get(&(p, t)).unwrap());
192 labor_used += labor_hours[&p] * solution.value(*x.get(&(p, t)).unwrap());
193 }
194
195 let machine_util = machine_used / machine_capacity[&t] * 100.0;
196 let labor_util = labor_used / labor_capacity[&t] * 100.0;
197
198 println!("{:<10} {:.1}/{} ({:.1}%) {:<5} {:.1}/{} ({:.1}%)",
199 t,
200 machine_used,
201 machine_capacity[&t],
202 machine_util,
203 "",
204 labor_used,
205 labor_capacity[&t],
206 labor_util);
207 }
208
209 },
210 Err(e) => {
211 println!("No optimal solution found. Error: {:?}", e);
212 }
213 }
214
215 // Print elapsed time
216 let elapsed = start_time.elapsed();
217 println!("\nTime elapsed: {:?}", elapsed);
218}