supplycm 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- supplycm/__init__.py +34 -0
- supplycm/blockchain/__init__.py +6 -0
- supplycm/blockchain/hash_chain.py +35 -0
- supplycm/blockchain/provenance_tracking.py +27 -0
- supplycm/blockchain/smart_contract_check.py +42 -0
- supplycm/blockchain/verify_chain.py +32 -0
- supplycm/contracts/__init__.py +5 -0
- supplycm/contracts/buyback_contract.py +28 -0
- supplycm/contracts/quantity_flexibility_contract.py +28 -0
- supplycm/contracts/revenue_sharing_contract.py +26 -0
- supplycm/cost/__init__.py +4 -0
- supplycm/cost/landed_cost.py +19 -0
- supplycm/cost/total_cost_procurement.py +14 -0
- supplycm/demand/__init__.py +10 -0
- supplycm/demand/cannibalization_effect.py +16 -0
- supplycm/demand/demand_aggregation.py +17 -0
- supplycm/demand/demand_class_abc_xyz.py +20 -0
- supplycm/demand/demand_disaggregation.py +17 -0
- supplycm/demand/demand_sensing.py +21 -0
- supplycm/demand/promotional_demand_lift.py +14 -0
- supplycm/demand/seasonality_index.py +23 -0
- supplycm/demand/stockout_demand_loss.py +15 -0
- supplycm/demand/trend_seasonal_decomposition_forecast.py +55 -0
- supplycm/forecasting/__init__.py +51 -0
- supplycm/forecasting/adf_test.py +32 -0
- supplycm/forecasting/akaike_information_criterion.py +22 -0
- supplycm/forecasting/ar_model.py +40 -0
- supplycm/forecasting/autocorrelation.py +25 -0
- supplycm/forecasting/average_method.py +15 -0
- supplycm/forecasting/bates_granger_combination.py +37 -0
- supplycm/forecasting/bayesian_information_criterion.py +19 -0
- supplycm/forecasting/bottom_up_reconciliation.py +36 -0
- supplycm/forecasting/box_cox_transform.py +20 -0
- supplycm/forecasting/brier_score.py +17 -0
- supplycm/forecasting/browns_double_exponential.py +25 -0
- supplycm/forecasting/browns_triple_exponential.py +33 -0
- supplycm/forecasting/classical_decomposition.py +70 -0
- supplycm/forecasting/croston_with_decay.py +24 -0
- supplycm/forecasting/crostons_method.py +46 -0
- supplycm/forecasting/dampened_trend.py +44 -0
- supplycm/forecasting/doubling_seasonal_smoothing.py +34 -0
- supplycm/forecasting/drift_method.py +16 -0
- supplycm/forecasting/exponential_trend_forecast.py +30 -0
- supplycm/forecasting/gompertz_trend.py +42 -0
- supplycm/forecasting/holt_linear_trend.py +36 -0
- supplycm/forecasting/holt_winters.py +56 -0
- supplycm/forecasting/hurst_exponent.py +53 -0
- supplycm/forecasting/inverse_box_cox.py +15 -0
- supplycm/forecasting/kpss_test.py +28 -0
- supplycm/forecasting/linear_regression_forecast.py +33 -0
- supplycm/forecasting/ljung_box_test.py +25 -0
- supplycm/forecasting/logistic_trend.py +44 -0
- supplycm/forecasting/ma_model.py +40 -0
- supplycm/forecasting/moving_median_filter.py +26 -0
- supplycm/forecasting/mstl_decomposition.py +43 -0
- supplycm/forecasting/naive_forecast.py +14 -0
- supplycm/forecasting/partial_autocorrelation.py +41 -0
- supplycm/forecasting/pegels_classification.py +37 -0
- supplycm/forecasting/polynomial_regression_forecast.py +48 -0
- supplycm/forecasting/rolling_mean_forecast.py +18 -0
- supplycm/forecasting/sba_method.py +43 -0
- supplycm/forecasting/seasonal_indices.py +30 -0
- supplycm/forecasting/seasonal_naive_forecast.py +20 -0
- supplycm/forecasting/seasonal_trend_loess.py +45 -0
- supplycm/forecasting/ses_with_drift.py +24 -0
- supplycm/forecasting/simple_moving_average.py +29 -0
- supplycm/forecasting/single_exponential_smoothing.py +28 -0
- supplycm/forecasting/theils_u.py +20 -0
- supplycm/forecasting/theta_method.py +42 -0
- supplycm/forecasting/top_down_reconciliation.py +21 -0
- supplycm/forecasting/tracking_signal.py +24 -0
- supplycm/forecasting/tsb_method.py +34 -0
- supplycm/forecasting/var_model.py +64 -0
- supplycm/forecasting/weighted_moving_average.py +35 -0
- supplycm/inventory/__init__.py +61 -0
- supplycm/inventory/abc_analysis.py +34 -0
- supplycm/inventory/abc_xyz_matrix.py +21 -0
- supplycm/inventory/aging_schedule.py +29 -0
- supplycm/inventory/anticipation_inventory.py +19 -0
- supplycm/inventory/base_stock_policy.py +18 -0
- supplycm/inventory/bullwhip_effect.py +24 -0
- supplycm/inventory/cycle_service_level.py +16 -0
- supplycm/inventory/days_of_supply.py +13 -0
- supplycm/inventory/dead_stock_identification.py +17 -0
- supplycm/inventory/decoupling_inventory.py +12 -0
- supplycm/inventory/demand_during_lead_time.py +13 -0
- supplycm/inventory/economic_order_quantity.py +35 -0
- supplycm/inventory/economic_production_quantity.py +21 -0
- supplycm/inventory/eoq_quantity_discount.py +42 -0
- supplycm/inventory/eoq_with_backorders.py +16 -0
- supplycm/inventory/expected_backorder.py +19 -0
- supplycm/inventory/expected_on_hand.py +13 -0
- supplycm/inventory/fifo_valuation.py +34 -0
- supplycm/inventory/fill_rate_calculation.py +27 -0
- supplycm/inventory/fixed_order_quantity.py +26 -0
- supplycm/inventory/gmroi.py +13 -0
- supplycm/inventory/holding_cost_calculation.py +11 -0
- supplycm/inventory/inventory_carrying_rate.py +14 -0
- supplycm/inventory/inventory_position.py +11 -0
- supplycm/inventory/inventory_to_sales_ratio.py +13 -0
- supplycm/inventory/inventory_turnover_ratio.py +13 -0
- supplycm/inventory/joint_replenishment.py +27 -0
- supplycm/inventory/least_period_cost.py +34 -0
- supplycm/inventory/least_unit_cost.py +34 -0
- supplycm/inventory/lifo_valuation.py +26 -0
- supplycm/inventory/lot_for_lot.py +14 -0
- supplycm/inventory/marginal_analysis_newsvendor.py +32 -0
- supplycm/inventory/multi_echelon_inventory.py +36 -0
- supplycm/inventory/newsvendor_model.py +40 -0
- supplycm/inventory/obsolescence_cost.py +14 -0
- supplycm/inventory/optimal_stockout_probability.py +19 -0
- supplycm/inventory/ordering_cost_allocation.py +15 -0
- supplycm/inventory/part_period_balancing.py +35 -0
- supplycm/inventory/periodic_order_quantity.py +30 -0
- supplycm/inventory/periodic_review_policy.py +20 -0
- supplycm/inventory/perishable_inventory.py +32 -0
- supplycm/inventory/pipeline_inventory.py +13 -0
- supplycm/inventory/r_q_policy.py +30 -0
- supplycm/inventory/reorder_point.py +13 -0
- supplycm/inventory/risk_pooling.py +25 -0
- supplycm/inventory/s_s_policy.py +31 -0
- supplycm/inventory/safety_stock_normal.py +14 -0
- supplycm/inventory/safety_stock_with_lead_time_var.py +16 -0
- supplycm/inventory/silver_meal.py +34 -0
- supplycm/inventory/slow_moving_detection.py +21 -0
- supplycm/inventory/spare_parts_fsn.py +24 -0
- supplycm/inventory/spare_parts_hml.py +18 -0
- supplycm/inventory/spare_parts_sde.py +18 -0
- supplycm/inventory/spare_parts_ved.py +18 -0
- supplycm/inventory/square_root_law.py +17 -0
- supplycm/inventory/stockout_cost.py +13 -0
- supplycm/inventory/vendor_managed_inventory.py +16 -0
- supplycm/inventory/wagner_whitin.py +42 -0
- supplycm/inventory/weighted_average_cost.py +16 -0
- supplycm/inventory/xyz_analysis.py +30 -0
- supplycm/iot/__init__.py +6 -0
- supplycm/iot/anomaly_detection.py +33 -0
- supplycm/iot/iot_data_aggregation.py +52 -0
- supplycm/iot/realtime_inventory_monitor.py +44 -0
- supplycm/iot/sensor_data_smoothing.py +27 -0
- supplycm/lean/__init__.py +6 -0
- supplycm/lean/cycle_time_efficiency.py +13 -0
- supplycm/lean/oee.py +11 -0
- supplycm/lean/takt_time.py +13 -0
- supplycm/lean/wip_calculation.py +13 -0
- supplycm/mrp/__init__.py +31 -0
- supplycm/mrp/available_to_promise.py +35 -0
- supplycm/mrp/backflush.py +17 -0
- supplycm/mrp/bom_explosion.py +29 -0
- supplycm/mrp/capable_to_promise.py +33 -0
- supplycm/mrp/capacity_requirements_planning.py +36 -0
- supplycm/mrp/cycle_counting.py +28 -0
- supplycm/mrp/demand_time_fence.py +15 -0
- supplycm/mrp/drum_buffer_rope.py +27 -0
- supplycm/mrp/kaban_sizing.py +16 -0
- supplycm/mrp/lead_time_offsetting.py +19 -0
- supplycm/mrp/lot_size_rule_epr.py +29 -0
- supplycm/mrp/lot_size_rule_foq.py +19 -0
- supplycm/mrp/lot_size_rule_l4l.py +12 -0
- supplycm/mrp/lot_size_rule_poq.py +18 -0
- supplycm/mrp/low_level_coding.py +32 -0
- supplycm/mrp/master_production_schedule.py +28 -0
- supplycm/mrp/maximum_order_quantity.py +23 -0
- supplycm/mrp/minimum_order_quantity.py +12 -0
- supplycm/mrp/modular_bom.py +25 -0
- supplycm/mrp/mrp_calculation.py +44 -0
- supplycm/mrp/order_multiples.py +15 -0
- supplycm/mrp/pegging.py +27 -0
- supplycm/mrp/phantom_bom_handling.py +26 -0
- supplycm/mrp/planning_bom.py +13 -0
- supplycm/mrp/planning_time_fence.py +26 -0
- supplycm/mrp/quantity_discount_mrp.py +23 -0
- supplycm/mrp/rough_cut_capacity_planning.py +33 -0
- supplycm/mrp/safety_lead_time.py +14 -0
- supplycm/mrp/shrinkage_factor.py +14 -0
- supplycm/mrp/where_used_query.py +24 -0
- supplycm/network/__init__.py +30 -0
- supplycm/network/a_star_search.py +29 -0
- supplycm/network/all_pairs_shortest_path.py +32 -0
- supplycm/network/articulation_points.py +39 -0
- supplycm/network/bellman_ford.py +36 -0
- supplycm/network/betweenness_centrality.py +43 -0
- supplycm/network/bfs_shortest_path.py +23 -0
- supplycm/network/bidirectional_search.py +57 -0
- supplycm/network/bipartite_matching.py +28 -0
- supplycm/network/bridges_in_graph.py +32 -0
- supplycm/network/closeness_centrality.py +3 -0
- supplycm/network/connected_components.py +26 -0
- supplycm/network/degree_centrality.py +15 -0
- supplycm/network/dfs_traversal.py +25 -0
- supplycm/network/dijkstra_shortest_path.py +41 -0
- supplycm/network/edmonds_karp_max_flow.py +43 -0
- supplycm/network/eigenvector_centrality.py +30 -0
- supplycm/network/floyd_warshall.py +20 -0
- supplycm/network/ford_fulkerson_max_flow.py +34 -0
- supplycm/network/kruskal_mst.py +41 -0
- supplycm/network/max_weight_bipartite_matching.py +17 -0
- supplycm/network/maximal_clique_bron_kerbosch.py +24 -0
- supplycm/network/min_cost_flow_cycle_canceling.py +67 -0
- supplycm/network/min_cut_max_flow_theorem.py +57 -0
- supplycm/network/min_cut_stoer_wagner.py +54 -0
- supplycm/network/min_weight_bipartite_matching.py +14 -0
- supplycm/network/page_rank.py +35 -0
- supplycm/network/prim_mst.py +31 -0
- supplycm/network/strongly_connected_components.py +43 -0
- supplycm/network/successive_shortest_path.py +69 -0
- supplycm/network/topological_sort.py +26 -0
- supplycm/network_design/__init__.py +7 -0
- supplycm/network_design/break_even_analysis.py +23 -0
- supplycm/network_design/center_of_gravity.py +24 -0
- supplycm/network_design/facility_location_fixed_cost.py +48 -0
- supplycm/network_design/network_reliability.py +27 -0
- supplycm/network_design/single_source_allocation.py +42 -0
- supplycm/optimization/__init__.py +31 -0
- supplycm/optimization/ant_colony_optimization.py +62 -0
- supplycm/optimization/bin_packing_best_fit.py +26 -0
- supplycm/optimization/bin_packing_first_fit.py +23 -0
- supplycm/optimization/bin_packing_first_fit_decreasing.py +15 -0
- supplycm/optimization/binary_search.py +21 -0
- supplycm/optimization/branch_and_bound.py +47 -0
- supplycm/optimization/convex_hull.py +28 -0
- supplycm/optimization/dynamic_programming_lcs.py +31 -0
- supplycm/optimization/edit_distance.py +24 -0
- supplycm/optimization/fractional_knapsack.py +29 -0
- supplycm/optimization/genetic_algorithm.py +42 -0
- supplycm/optimization/golden_section_search.py +28 -0
- supplycm/optimization/gradient_descent.py +33 -0
- supplycm/optimization/graph_coloring_greedy.py +23 -0
- supplycm/optimization/heap_sort.py +15 -0
- supplycm/optimization/knapsack_01_dp.py +36 -0
- supplycm/optimization/lagrange_multiplier.py +33 -0
- supplycm/optimization/longest_increasing_subsequence.py +22 -0
- supplycm/optimization/matrix_chain_multiplication.py +22 -0
- supplycm/optimization/merge_sort.py +30 -0
- supplycm/optimization/n_queens_backtracking.py +29 -0
- supplycm/optimization/newton_raphson.py +22 -0
- supplycm/optimization/p_median.py +29 -0
- supplycm/optimization/particle_swarm_optimization.py +54 -0
- supplycm/optimization/quick_sort.py +18 -0
- supplycm/optimization/set_cover_greedy.py +23 -0
- supplycm/optimization/simplex_method.py +63 -0
- supplycm/optimization/simulated_annealing.py +40 -0
- supplycm/optimization/subset_sum.py +20 -0
- supplycm/optimization/tabu_search.py +45 -0
- supplycm/quality/__init__.py +9 -0
- supplycm/quality/dpmo.py +13 -0
- supplycm/quality/p_chart.py +25 -0
- supplycm/quality/process_capability_cp.py +14 -0
- supplycm/quality/process_capability_cpk.py +16 -0
- supplycm/quality/r_chart.py +23 -0
- supplycm/quality/sigma_level.py +20 -0
- supplycm/quality/x_bar_chart.py +30 -0
- supplycm/risk/__init__.py +2 -0
- supplycm/risk/supply_chain_resilience_index.py +23 -0
- supplycm/routing/__init__.py +31 -0
- supplycm/routing/assignment_problem_hungarian.py +70 -0
- supplycm/routing/chinese_postman.py +59 -0
- supplycm/routing/dial_a_ride.py +38 -0
- supplycm/routing/eulerian_tour.py +26 -0
- supplycm/routing/hamiltonian_path_backtrack.py +34 -0
- supplycm/routing/least_cost_method.py +37 -0
- supplycm/routing/multi_depot_vrp.py +62 -0
- supplycm/routing/northwest_corner_method.py +28 -0
- supplycm/routing/periodic_vrp.py +31 -0
- supplycm/routing/pickup_delivery_problem.py +57 -0
- supplycm/routing/rural_postman.py +34 -0
- supplycm/routing/split_delivery_vrp.py +44 -0
- supplycm/routing/steiner_tree.py +44 -0
- supplycm/routing/transportation_simplex_modi.py +69 -0
- supplycm/routing/transshipment_problem.py +30 -0
- supplycm/routing/tsp_cheapest_insertion.py +33 -0
- supplycm/routing/tsp_christofides.py +88 -0
- supplycm/routing/tsp_farthest_insertion.py +41 -0
- supplycm/routing/tsp_held_karp.py +56 -0
- supplycm/routing/tsp_nearest_insertion.py +32 -0
- supplycm/routing/tsp_nearest_neighbor.py +42 -0
- supplycm/routing/tsp_three_opt.py +46 -0
- supplycm/routing/tsp_two_opt.py +36 -0
- supplycm/routing/vehicle_scheduling.py +30 -0
- supplycm/routing/vogels_approximation.py +72 -0
- supplycm/routing/vrp_capacitated_greedy.py +35 -0
- supplycm/routing/vrp_cluster_first_route_second.py +49 -0
- supplycm/routing/vrp_savings.py +70 -0
- supplycm/routing/vrp_sweep.py +47 -0
- supplycm/routing/vrp_with_time_windows.py +59 -0
- supplycm/scheduling/__init__.py +41 -0
- supplycm/scheduling/batch_scheduling.py +21 -0
- supplycm/scheduling/cmax_calculation.py +22 -0
- supplycm/scheduling/critical_path_method.py +34 -0
- supplycm/scheduling/critical_ratio.py +16 -0
- supplycm/scheduling/deteriorating_jobs_scheduling.py +15 -0
- supplycm/scheduling/earliest_start_schedule.py +27 -0
- supplycm/scheduling/edd_rule.py +12 -0
- supplycm/scheduling/fcfs_rule.py +12 -0
- supplycm/scheduling/flow_shop_schedule.py +20 -0
- supplycm/scheduling/gantt_chart_data.py +21 -0
- supplycm/scheduling/job_shop_schedule.py +45 -0
- supplycm/scheduling/johnsons_rule.py +42 -0
- supplycm/scheduling/learning_curve_scheduling.py +14 -0
- supplycm/scheduling/least_slack.py +15 -0
- supplycm/scheduling/line_balancing.py +38 -0
- supplycm/scheduling/list_scheduling.py +20 -0
- supplycm/scheduling/lpt_rule.py +20 -0
- supplycm/scheduling/lrpt_rule.py +14 -0
- supplycm/scheduling/machine_utilization.py +14 -0
- supplycm/scheduling/moore_hodgson.py +25 -0
- supplycm/scheduling/multifit_algorithm.py +46 -0
- supplycm/scheduling/neh_heuristic.py +46 -0
- supplycm/scheduling/no_wait_scheduling.py +33 -0
- supplycm/scheduling/open_shop_schedule.py +44 -0
- supplycm/scheduling/parallel_machine_cmax.py +16 -0
- supplycm/scheduling/parallel_station_scheduling.py +24 -0
- supplycm/scheduling/pert_expected_duration.py +22 -0
- supplycm/scheduling/preemptive_spt.py +42 -0
- supplycm/scheduling/resource_constrained_scheduling.py +62 -0
- supplycm/scheduling/round_robin_scheduling.py +49 -0
- supplycm/scheduling/rpw_priority.py +25 -0
- supplycm/scheduling/setup_time_aware_scheduling.py +23 -0
- supplycm/scheduling/slack_time_calculation.py +32 -0
- supplycm/scheduling/spt_rule.py +12 -0
- supplycm/scheduling/srpt_rule.py +14 -0
- supplycm/scheduling/tardiness_calculation.py +19 -0
- supplycm/scheduling/total_completion_time.py +17 -0
- supplycm/scheduling/total_weighted_tardiness.py +19 -0
- supplycm/scheduling/two_machine_open_shop.py +23 -0
- supplycm/scheduling/wspt_rule.py +13 -0
- supplycm/simulation/__init__.py +4 -0
- supplycm/simulation/monte_carlo_inventory.py +35 -0
- supplycm/simulation/monte_carlo_risk.py +41 -0
- supplycm/sop/__init__.py +5 -0
- supplycm/sop/demand_supply_match.py +22 -0
- supplycm/sop/production_chase_strategy.py +19 -0
- supplycm/sop/production_level_strategy.py +16 -0
- supplycm/statistics/__init__.py +41 -0
- supplycm/statistics/adjusted_r_squared.py +13 -0
- supplycm/statistics/anderson_darling_test.py +29 -0
- supplycm/statistics/anova_one_way.py +28 -0
- supplycm/statistics/bias.py +15 -0
- supplycm/statistics/bootstrap_confidence_interval.py +28 -0
- supplycm/statistics/chi_square_goodness_of_fit.py +20 -0
- supplycm/statistics/coefficient_of_determination.py +20 -0
- supplycm/statistics/coefficient_of_variation.py +19 -0
- supplycm/statistics/confidence_interval_mean.py +20 -0
- supplycm/statistics/correlation.py +22 -0
- supplycm/statistics/descriptive_stats.py +42 -0
- supplycm/statistics/diebold_mariano_test.py +24 -0
- supplycm/statistics/exponential_smooth.py +19 -0
- supplycm/statistics/f_test_variance.py +21 -0
- supplycm/statistics/forecast_value_added.py +17 -0
- supplycm/statistics/jarque_bera_test.py +23 -0
- supplycm/statistics/kolmogorov_smirnov_test.py +24 -0
- supplycm/statistics/kolmogorov_smirnov_two_sample.py +29 -0
- supplycm/statistics/kurtosis.py +20 -0
- supplycm/statistics/mae.py +15 -0
- supplycm/statistics/mann_whitney_u.py +30 -0
- supplycm/statistics/mape.py +23 -0
- supplycm/statistics/mase.py +21 -0
- supplycm/statistics/mean_percentage_error.py +23 -0
- supplycm/statistics/minmax_scale.py +19 -0
- supplycm/statistics/moving_average_smooth.py +19 -0
- supplycm/statistics/mse.py +15 -0
- supplycm/statistics/outlier_detection_iqr.py +29 -0
- supplycm/statistics/percent_bias.py +18 -0
- supplycm/statistics/r_squared.py +20 -0
- supplycm/statistics/rmse.py +16 -0
- supplycm/statistics/shapiro_wilk_approx.py +27 -0
- supplycm/statistics/skewness.py +20 -0
- supplycm/statistics/smape.py +20 -0
- supplycm/statistics/spearman_correlation.py +33 -0
- supplycm/statistics/t_test_one_sample.py +21 -0
- supplycm/statistics/t_test_two_sample.py +23 -0
- supplycm/statistics/tracking_signal_threshold.py +13 -0
- supplycm/statistics/wilcoxon_signed_rank.py +27 -0
- supplycm/statistics/zscore.py +21 -0
- supplycm/supplier/__init__.py +31 -0
- supplycm/supplier/ahp_supplier_selection.py +20 -0
- supplycm/supplier/analytic_network_process.py +25 -0
- supplycm/supplier/competitive_bidding.py +31 -0
- supplycm/supplier/contract_compliance_score.py +14 -0
- supplycm/supplier/data_envelopment_analysis.py +42 -0
- supplycm/supplier/electre.py +45 -0
- supplycm/supplier/fuzzy_topsis.py +31 -0
- supplycm/supplier/lead_time_quoted_vs_actual.py +18 -0
- supplycm/supplier/maverick_spend_detection.py +18 -0
- supplycm/supplier/negotiation_zone.py +17 -0
- supplycm/supplier/on_time_delivery_rate.py +18 -0
- supplycm/supplier/preferred_supplier_index.py +23 -0
- supplycm/supplier/price_analysis.py +24 -0
- supplycm/supplier/promethee.py +37 -0
- supplycm/supplier/purchase_order_compliance.py +16 -0
- supplycm/supplier/purchase_price_variance.py +13 -0
- supplycm/supplier/should_cost_analysis.py +18 -0
- supplycm/supplier/spend_analysis.py +22 -0
- supplycm/supplier/strategic_supplier_scorecard.py +19 -0
- supplycm/supplier/supplier_audit_score.py +23 -0
- supplycm/supplier/supplier_consolidation.py +20 -0
- supplycm/supplier/supplier_diversity_index.py +16 -0
- supplycm/supplier/supplier_evaluation_matrix.py +19 -0
- supplycm/supplier/supplier_rating.py +16 -0
- supplycm/supplier/supplier_risk_score.py +18 -0
- supplycm/supplier/supplier_segmentation.py +29 -0
- supplycm/supplier/topsis.py +51 -0
- supplycm/supplier/total_cost_of_ownership.py +15 -0
- supplycm/supplier/vendor_scorecard.py +27 -0
- supplycm/supplier/weighted_point_method.py +14 -0
- supplycm/sustainability/__init__.py +5 -0
- supplycm/sustainability/carbon_footprint_transport.py +14 -0
- supplycm/sustainability/energy_consumption_warehouse.py +15 -0
- supplycm/sustainability/reverse_logistics_cost.py +16 -0
- supplycm/warehouse/__init__.py +11 -0
- supplycm/warehouse/cross_dock_scheduling.py +34 -0
- supplycm/warehouse/dock_door_assignment.py +25 -0
- supplycm/warehouse/order_picking_wave.py +29 -0
- supplycm/warehouse/pallet_building.py +29 -0
- supplycm/warehouse/putaway_strategy.py +30 -0
- supplycm/warehouse/return_routing.py +24 -0
- supplycm/warehouse/s_shape_routing.py +18 -0
- supplycm/warehouse/traveling_salesman_picking.py +27 -0
- supplycm/warehouse/warehouse_layout_optimization.py +23 -0
- supplycm/warehouse/warehouse_slotting_abc.py +29 -0
- supplycm-1.2.1.dist-info/METADATA +138 -0
- supplycm-1.2.1.dist-info/RECORD +425 -0
- supplycm-1.2.1.dist-info/WHEEL +5 -0
- supplycm-1.2.1.dist-info/licenses/LICENSE +21 -0
- supplycm-1.2.1.dist-info/top_level.txt +1 -0
supplycm/__init__.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""supplycm - Supply Chain Management algorithms (pure Python).
|
|
2
|
+
|
|
3
|
+
A collection of 360 patent-free / public-domain algorithms for
|
|
4
|
+
forecasting, inventory, transportation, network optimization,
|
|
5
|
+
scheduling, MRP, statistics, optimization, supplier management,
|
|
6
|
+
warehouse operations, demand planning and risk assessment.
|
|
7
|
+
|
|
8
|
+
Version 1.2.1 - First stable release with full test coverage.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "1.2.1"
|
|
12
|
+
|
|
13
|
+
from . import forecasting
|
|
14
|
+
from . import inventory
|
|
15
|
+
from . import statistics
|
|
16
|
+
from . import routing
|
|
17
|
+
from . import network
|
|
18
|
+
from . import scheduling
|
|
19
|
+
from . import mrp
|
|
20
|
+
from . import optimization
|
|
21
|
+
from . import supplier
|
|
22
|
+
from . import warehouse
|
|
23
|
+
from . import demand
|
|
24
|
+
from . import risk
|
|
25
|
+
from . import quality
|
|
26
|
+
from . import lean
|
|
27
|
+
from . import sop
|
|
28
|
+
from . import sustainability
|
|
29
|
+
from . import contracts
|
|
30
|
+
from . import simulation
|
|
31
|
+
from . import cost
|
|
32
|
+
from . import network_design
|
|
33
|
+
from . import iot
|
|
34
|
+
from . import blockchain
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Simple hash chain for supply chain traceability."""
|
|
2
|
+
import hashlib
|
|
3
|
+
from typing import List, Dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def hash_chain(records: List[Dict]) -> List[str]:
|
|
7
|
+
"""Create a hash chain linking supply chain events.
|
|
8
|
+
|
|
9
|
+
Each record hash includes the previous hash, creating an
|
|
10
|
+
immutable chain. Tampering with any record breaks the chain.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
records: List of event dicts (e.g., timestamp, location, product).
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
List of hash strings, one per record.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> hashes = hash_chain([
|
|
20
|
+
... {'event': 'manufactured', 'product': 'A'},
|
|
21
|
+
... {'event': 'shipped', 'product': 'A'},
|
|
22
|
+
... {'event': 'delivered', 'product': 'A'},
|
|
23
|
+
... ])
|
|
24
|
+
>>> len(hashes) == 3
|
|
25
|
+
True
|
|
26
|
+
"""
|
|
27
|
+
hashes = []
|
|
28
|
+
prev_hash = '0' * 64 # Genesis block
|
|
29
|
+
for record in records:
|
|
30
|
+
# Sort keys for deterministic hashing
|
|
31
|
+
record_str = str(sorted(record.items())) + prev_hash
|
|
32
|
+
current_hash = hashlib.sha256(record_str.encode()).hexdigest()
|
|
33
|
+
hashes.append(current_hash)
|
|
34
|
+
prev_hash = current_hash
|
|
35
|
+
return hashes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Track product provenance through the supply chain."""
|
|
2
|
+
from typing import List, Dict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def provenance_tracking(events: List[Dict], product_id: str) -> List[Dict]:
|
|
6
|
+
"""Extract the full history of a product from supply chain events.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
events: List of all supply chain events.
|
|
10
|
+
product_id: Product to track.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
Chronological list of events for the product.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> events = [
|
|
17
|
+
... {'product': 'A', 'event': 'manufactured', 'time': 1},
|
|
18
|
+
... {'product': 'B', 'event': 'manufactured', 'time': 2},
|
|
19
|
+
... {'product': 'A', 'event': 'shipped', 'time': 3},
|
|
20
|
+
... ]
|
|
21
|
+
>>> history = provenance_tracking(events, 'A')
|
|
22
|
+
>>> len(history) == 2
|
|
23
|
+
True
|
|
24
|
+
"""
|
|
25
|
+
history = [e for e in events if e.get('product') == product_id]
|
|
26
|
+
history.sort(key=lambda e: e.get('time', 0))
|
|
27
|
+
return history
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Simple smart contract validation for supply chain rules."""
|
|
2
|
+
from typing import Dict, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def smart_contract_check(transaction: Dict, rules: List[Dict]) -> Dict:
|
|
6
|
+
"""Validate a transaction against supply chain rules.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
transaction: Dict with transaction details.
|
|
10
|
+
rules: List of rule dicts with 'field', 'operator', 'value'.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
Dict with 'valid' (bool) and 'violations' (list).
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> result = smart_contract_check(
|
|
17
|
+
... {'temperature': 5, 'humidity': 60},
|
|
18
|
+
... [{'field': 'temperature', 'operator': '<=', 'value': 8},
|
|
19
|
+
... {'field': 'humidity', 'operator': '>=', 'value': 40}])
|
|
20
|
+
>>> result['valid']
|
|
21
|
+
True
|
|
22
|
+
"""
|
|
23
|
+
violations = []
|
|
24
|
+
for rule in rules:
|
|
25
|
+
field = rule['field']
|
|
26
|
+
op = rule['operator']
|
|
27
|
+
value = rule['value']
|
|
28
|
+
actual = transaction.get(field)
|
|
29
|
+
if actual is None:
|
|
30
|
+
violations.append(f'Missing field: {field}')
|
|
31
|
+
continue
|
|
32
|
+
if op == '<=' and not (actual <= value):
|
|
33
|
+
violations.append(f'{field}={actual} exceeds {value}')
|
|
34
|
+
elif op == '>=' and not (actual >= value):
|
|
35
|
+
violations.append(f'{field}={actual} below {value}')
|
|
36
|
+
elif op == '==' and not (actual == value):
|
|
37
|
+
violations.append(f'{field}={actual} not equal to {value}')
|
|
38
|
+
elif op == '<' and not (actual < value):
|
|
39
|
+
violations.append(f'{field}={actual} not less than {value}')
|
|
40
|
+
elif op == '>' and not (actual > value):
|
|
41
|
+
violations.append(f'{field}={actual} not greater than {value}')
|
|
42
|
+
return {'valid': len(violations) == 0, 'violations': violations}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Verify integrity of a hash chain."""
|
|
2
|
+
import hashlib
|
|
3
|
+
from typing import List, Dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def verify_chain(records: List[Dict], hashes: List[str]) -> bool:
|
|
7
|
+
"""Verify that a hash chain is valid (not tampered with).
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
records: Original event records.
|
|
11
|
+
hashes: Hash chain to verify.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
True if chain is valid, False otherwise.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> from supplycm.blockchain.hash_chain import hash_chain
|
|
18
|
+
>>> records = [{'event': 'created'}, {'event': 'shipped'}]
|
|
19
|
+
>>> hashes = hash_chain(records)
|
|
20
|
+
>>> verify_chain(records, hashes)
|
|
21
|
+
True
|
|
22
|
+
"""
|
|
23
|
+
if len(records) != len(hashes):
|
|
24
|
+
return False
|
|
25
|
+
prev_hash = '0' * 64
|
|
26
|
+
for record, expected_hash in zip(records, hashes):
|
|
27
|
+
record_str = str(sorted(record.items())) + prev_hash
|
|
28
|
+
actual_hash = hashlib.sha256(record_str.encode()).hexdigest()
|
|
29
|
+
if actual_hash != expected_hash:
|
|
30
|
+
return False
|
|
31
|
+
prev_hash = expected_hash
|
|
32
|
+
return True
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Buyback contract analysis."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def buyback_contract(wholesale_price: float, retail_price: float,
|
|
5
|
+
buyback_price: float, demand: float,
|
|
6
|
+
unit_cost: float, salvage_value: float = 0) -> dict:
|
|
7
|
+
"""Analyze buyback contract.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> result = buyback_contract(50, 100, 30, 800, 20, 10)
|
|
11
|
+
>>> isinstance(result['supplier_profit'], float)
|
|
12
|
+
True
|
|
13
|
+
"""
|
|
14
|
+
if buyback_price > wholesale_price:
|
|
15
|
+
raise ValueError("buyback should not exceed wholesale")
|
|
16
|
+
order_qty = demand
|
|
17
|
+
unsold = max(0, order_qty - demand)
|
|
18
|
+
supplier_revenue = wholesale_price * order_qty - buyback_price * unsold + salvage_value * unsold
|
|
19
|
+
supplier_cost = unit_cost * order_qty
|
|
20
|
+
supplier_profit = supplier_revenue - supplier_cost
|
|
21
|
+
retailer_revenue = retail_price * demand + buyback_price * unsold
|
|
22
|
+
retailer_cost = wholesale_price * order_qty
|
|
23
|
+
retailer_profit = retailer_revenue - retailer_cost
|
|
24
|
+
return {
|
|
25
|
+
"supplier_profit": supplier_profit,
|
|
26
|
+
"retailer_profit": retailer_profit,
|
|
27
|
+
"total_profit": supplier_profit + retailer_profit,
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Quantity flexibility contract analysis."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def quantity_flexibility_contract(wholesale_price: float, retail_price: float,
|
|
5
|
+
order_quantity: float, actual_demand: float,
|
|
6
|
+
unit_cost: float, flexibility_fraction: float = 0.2) -> dict:
|
|
7
|
+
"""Analyze quantity flexibility contract.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> result = quantity_flexibility_contract(50, 100, 1000, 900, 20, 0.2)
|
|
11
|
+
>>> result['final_quantity'] <= 1000
|
|
12
|
+
True
|
|
13
|
+
"""
|
|
14
|
+
if flexibility_fraction < 0 or flexibility_fraction > 1:
|
|
15
|
+
raise ValueError("flexibility must be in [0, 1]")
|
|
16
|
+
min_qty = order_quantity * (1 - flexibility_fraction)
|
|
17
|
+
max_qty = order_quantity * (1 + flexibility_fraction)
|
|
18
|
+
final_qty = max(min_qty, min(max_qty, actual_demand))
|
|
19
|
+
sales = min(final_qty, actual_demand)
|
|
20
|
+
unsold = final_qty - sales
|
|
21
|
+
supplier_profit = wholesale_price * final_qty - unit_cost * final_qty
|
|
22
|
+
retailer_profit = retail_price * sales + 0 * unsold - wholesale_price * final_qty
|
|
23
|
+
return {
|
|
24
|
+
"final_quantity": final_qty,
|
|
25
|
+
"supplier_profit": supplier_profit,
|
|
26
|
+
"retailer_profit": retailer_profit,
|
|
27
|
+
"total_profit": supplier_profit + retailer_profit,
|
|
28
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Revenue sharing contract analysis."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def revenue_sharing_contract(wholesale_price: float, retail_price: float,
|
|
5
|
+
revenue_share_fraction: float,
|
|
6
|
+
demand: float, unit_cost: float) -> dict:
|
|
7
|
+
"""Analyze revenue sharing contract.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> result = revenue_sharing_contract(40, 100, 0.2, 1000, 20)
|
|
11
|
+
>>> result['supplier_profit'] > 0
|
|
12
|
+
True
|
|
13
|
+
"""
|
|
14
|
+
if revenue_share_fraction < 0 or revenue_share_fraction > 1:
|
|
15
|
+
raise ValueError("share must be in [0, 1]")
|
|
16
|
+
supplier_revenue = wholesale_price * demand + revenue_share_fraction * retail_price * demand
|
|
17
|
+
supplier_cost = unit_cost * demand
|
|
18
|
+
supplier_profit = supplier_revenue - supplier_cost
|
|
19
|
+
retailer_revenue = (1 - revenue_share_fraction) * retail_price * demand
|
|
20
|
+
retailer_cost = wholesale_price * demand
|
|
21
|
+
retailer_profit = retailer_revenue - retailer_cost
|
|
22
|
+
return {
|
|
23
|
+
"supplier_profit": supplier_profit,
|
|
24
|
+
"retailer_profit": retailer_profit,
|
|
25
|
+
"total_profit": supplier_profit + retailer_profit,
|
|
26
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Landed cost calculation."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def landed_cost(unit_price: float, freight: float, duty_rate: float = 0,
|
|
5
|
+
insurance_rate: float = 0, handling: float = 0,
|
|
6
|
+
customs_broker: float = 0, quantity: float = 1) -> float:
|
|
7
|
+
"""Total cost per unit including all logistics expenses.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> round(landed_cost(100, 500, 0.05, 0.01, 100, 50, 10), 2)
|
|
11
|
+
165.5
|
|
12
|
+
"""
|
|
13
|
+
if quantity <= 0:
|
|
14
|
+
raise ValueError("quantity must be positive")
|
|
15
|
+
subtotal = unit_price * quantity
|
|
16
|
+
duty = subtotal * duty_rate
|
|
17
|
+
insurance = subtotal * insurance_rate
|
|
18
|
+
total = subtotal + freight + duty + insurance + handling + customs_broker
|
|
19
|
+
return total / quantity
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Total Cost of Procurement (TCP)."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def total_cost_procurement(purchase_cost: float, ordering_cost: float,
|
|
5
|
+
holding_cost: float, stockout_cost: float = 0,
|
|
6
|
+
quality_cost: float = 0, admin_cost: float = 0) -> float:
|
|
7
|
+
"""Sum of all procurement-related costs.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> total_cost_procurement(100000, 5000, 8000, 2000, 1000, 3000)
|
|
11
|
+
119000
|
|
12
|
+
"""
|
|
13
|
+
return (purchase_cost + ordering_cost + holding_cost +
|
|
14
|
+
stockout_cost + quality_cost + admin_cost)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""supplycm.demand - Demand algorithms."""
|
|
2
|
+
from .demand_aggregation import demand_aggregation
|
|
3
|
+
from .demand_disaggregation import demand_disaggregation
|
|
4
|
+
from .demand_class_abc_xyz import demand_class_abc_xyz
|
|
5
|
+
from .promotional_demand_lift import promotional_demand_lift
|
|
6
|
+
from .cannibalization_effect import cannibalization_effect
|
|
7
|
+
from .stockout_demand_loss import stockout_demand_loss
|
|
8
|
+
from .demand_sensing import demand_sensing
|
|
9
|
+
from .seasonality_index import seasonality_index
|
|
10
|
+
from .trend_seasonal_decomposition_forecast import trend_seasonal_decomposition_forecast
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Compute cannibalization effect of new product on existing."""
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def cannibalization_effect(existing_before: float, existing_after: float,
|
|
6
|
+
new_product_sales: float) -> float:
|
|
7
|
+
"""Fraction of new product sales that came from existing product.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> round(cannibalization_effect(100, 80, 50), 4)
|
|
11
|
+
0.4
|
|
12
|
+
"""
|
|
13
|
+
lost_sales = existing_before - existing_after
|
|
14
|
+
if new_product_sales <= 0:
|
|
15
|
+
return 0.0
|
|
16
|
+
return lost_sales / new_product_sales
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Aggregate demand by time bucket."""
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def demand_aggregation(daily_demand: List[float], bucket_size: int) -> List[float]:
|
|
6
|
+
"""Aggregate daily demand into larger buckets.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> demand_aggregation([10, 20, 30, 40, 50, 60], 3)
|
|
10
|
+
[60.0, 150.0]
|
|
11
|
+
"""
|
|
12
|
+
if bucket_size <= 0:
|
|
13
|
+
raise ValueError("bucket_size must be positive")
|
|
14
|
+
result = []
|
|
15
|
+
for i in range(0, len(daily_demand), bucket_size):
|
|
16
|
+
result.append(sum(daily_demand[i:i + bucket_size]))
|
|
17
|
+
return result
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Combine ABC value with XYZ variability classification."""
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def demand_class_abc_xyz(abc: List[Tuple[str, str]],
|
|
6
|
+
xyz: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
|
|
7
|
+
"""Combine into 9-class matrix (AX, AY, AZ, BX, ...).
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> result = demand_class_abc_xyz([('a', 'A'), ('b', 'B')],
|
|
11
|
+
... [('a', 'X'), ('b', 'Y')])
|
|
12
|
+
>>> result[0] == ('a', 'AX')
|
|
13
|
+
True
|
|
14
|
+
"""
|
|
15
|
+
abc_dict = dict(abc)
|
|
16
|
+
xyz_dict = dict(xyz)
|
|
17
|
+
result = []
|
|
18
|
+
for item in sorted(set(abc_dict) | set(xyz_dict)):
|
|
19
|
+
result.append((item, abc_dict.get(item, 'C') + xyz_dict.get(item, 'Z')))
|
|
20
|
+
return result
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Disaggregate forecast using historical proportions."""
|
|
2
|
+
from typing import Dict, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def demand_disaggregation(aggregate_forecast: float,
|
|
6
|
+
proportions: Dict[str, float]) -> Dict[str, float]:
|
|
7
|
+
"""Split aggregate forecast by proportions.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> d = demand_disaggregation(1000, {'A': 0.5, 'B': 0.3, 'C': 0.2})
|
|
11
|
+
>>> d['A']
|
|
12
|
+
500.0
|
|
13
|
+
"""
|
|
14
|
+
total = sum(proportions.values())
|
|
15
|
+
if total == 0:
|
|
16
|
+
return {k: 0 for k in proportions}
|
|
17
|
+
return {k: aggregate_forecast * (v / total) for k, v in proportions.items()}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Demand sensing using short-term signal adjustment."""
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def demand_sensing(forecast: List[float], actual_recent: List[float],
|
|
6
|
+
adjustment_window: int = 3) -> List[float]:
|
|
7
|
+
"""Adjust forecast based on recent actuals.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> adj = demand_sensing([100, 100, 100], [110, 105], 2)
|
|
11
|
+
>>> adj[0] > 100
|
|
12
|
+
True
|
|
13
|
+
"""
|
|
14
|
+
if not actual_recent:
|
|
15
|
+
return forecast
|
|
16
|
+
avg_recent = sum(actual_recent[-adjustment_window:]) / min(len(actual_recent), adjustment_window)
|
|
17
|
+
avg_forecast_recent = sum(forecast[:adjustment_window]) / min(len(forecast), adjustment_window)
|
|
18
|
+
if avg_forecast_recent == 0:
|
|
19
|
+
return forecast
|
|
20
|
+
adjustment = avg_recent / avg_forecast_recent
|
|
21
|
+
return [f * adjustment for f in forecast]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Calculate promotional lift factor."""
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def promotional_demand_lift(baseline_demand: float, promotion_demand: float) -> float:
|
|
6
|
+
"""Lift factor = promotion_demand / baseline.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> promotional_demand_lift(100, 150)
|
|
10
|
+
1.5
|
|
11
|
+
"""
|
|
12
|
+
if baseline_demand <= 0:
|
|
13
|
+
raise ValueError("baseline demand must be positive")
|
|
14
|
+
return promotion_demand / baseline_demand
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Compute seasonality index per period."""
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def seasonality_index(demand_series: List[float], season_length: int) -> List[float]:
|
|
6
|
+
"""Seasonality index = period_avg / overall_avg.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> idx = seasonality_index([10, 20, 30, 40, 50, 60, 70, 80], 4)
|
|
10
|
+
>>> len(idx) == 4
|
|
11
|
+
True
|
|
12
|
+
"""
|
|
13
|
+
if season_length <= 0 or len(demand_series) < season_length:
|
|
14
|
+
raise ValueError("invalid season length or insufficient data")
|
|
15
|
+
overall_avg = sum(demand_series) / len(demand_series)
|
|
16
|
+
if overall_avg == 0:
|
|
17
|
+
return [1.0] * season_length
|
|
18
|
+
indices = []
|
|
19
|
+
for i in range(season_length):
|
|
20
|
+
period_values = demand_series[i::season_length]
|
|
21
|
+
avg = sum(period_values) / len(period_values)
|
|
22
|
+
indices.append(avg / overall_avg)
|
|
23
|
+
return indices
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Estimate demand permanently lost due to stockout."""
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def stockout_demand_loss(stockout_qty: float, stockout_duration: float,
|
|
6
|
+
substitution_rate: float = 0.5) -> float:
|
|
7
|
+
"""Lost demand = stockout_qty * (1 - substitution_rate).
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> stockout_demand_loss(100, 5, 0.5)
|
|
11
|
+
50.0
|
|
12
|
+
"""
|
|
13
|
+
if substitution_rate < 0 or substitution_rate > 1:
|
|
14
|
+
raise ValueError("substitution rate must be in [0, 1]")
|
|
15
|
+
return stockout_qty * (1 - substitution_rate)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Forecast using decomposed trend and seasonality."""
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def trend_seasonal_decomposition_forecast(data: List[float], season_length: int,
|
|
6
|
+
horizon: int) -> List[float]:
|
|
7
|
+
"""Decompose and forecast.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> fcst = trend_seasonal_decomposition_forecast(
|
|
11
|
+
... [10, 20, 30, 40, 50, 60, 70, 80], 4, 2)
|
|
12
|
+
>>> len(fcst) == 2
|
|
13
|
+
True
|
|
14
|
+
"""
|
|
15
|
+
n = len(data)
|
|
16
|
+
if n < 2 * season_length:
|
|
17
|
+
# Use simple linear regression
|
|
18
|
+
from .linear_regression_forecast import linear_regression_forecast
|
|
19
|
+
_, _, f = linear_regression_forecast(data, horizon)
|
|
20
|
+
return f
|
|
21
|
+
# Centered moving average
|
|
22
|
+
half = season_length // 2
|
|
23
|
+
trend = [None] * n
|
|
24
|
+
for t in range(half, n - half):
|
|
25
|
+
trend[t] = sum(data[t - half:t + half + 1]) / (2 * half + 1)
|
|
26
|
+
# Compute seasonal indices
|
|
27
|
+
detrended = [data[t] - trend[t] if trend[t] is not None else 0 for t in range(n)]
|
|
28
|
+
s_idx = [0.0] * season_length
|
|
29
|
+
counts = [0] * season_length
|
|
30
|
+
for t in range(n):
|
|
31
|
+
s_idx[t % season_length] += detrended[t]
|
|
32
|
+
counts[t % season_length] += 1
|
|
33
|
+
s_idx = [s_idx[i] / counts[i] if counts[i] else 0 for i in range(season_length)]
|
|
34
|
+
# Mean zero adjustment
|
|
35
|
+
mean_s = sum(s_idx) / season_length
|
|
36
|
+
s_idx = [s - mean_s for s in s_idx]
|
|
37
|
+
# Linear trend on the trend series (excluding None)
|
|
38
|
+
trend_points = [(t, trend[t]) for t in range(n) if trend[t] is not None]
|
|
39
|
+
if len(trend_points) < 2:
|
|
40
|
+
return [data[-1]] * horizon
|
|
41
|
+
xs = [p[0] for p in trend_points]
|
|
42
|
+
ys = [p[1] for p in trend_points]
|
|
43
|
+
mean_x = sum(xs) / len(xs)
|
|
44
|
+
mean_y = sum(ys) / len(ys)
|
|
45
|
+
num = sum((xs[i] - mean_x) * (ys[i] - mean_y) for i in range(len(xs)))
|
|
46
|
+
den = sum((x - mean_x) ** 2 for x in xs)
|
|
47
|
+
slope = num / den if den else 0
|
|
48
|
+
intercept = mean_y - slope * mean_x
|
|
49
|
+
forecasts = []
|
|
50
|
+
for h in range(horizon):
|
|
51
|
+
t = n + h
|
|
52
|
+
trend_val = intercept + slope * t
|
|
53
|
+
seasonal = s_idx[t % season_length]
|
|
54
|
+
forecasts.append(trend_val + seasonal)
|
|
55
|
+
return forecasts
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""supplycm.forecasting - Forecasting algorithms."""
|
|
2
|
+
from .simple_moving_average import simple_moving_average
|
|
3
|
+
from .weighted_moving_average import weighted_moving_average
|
|
4
|
+
from .single_exponential_smoothing import single_exponential_smoothing
|
|
5
|
+
from .holt_linear_trend import holt_linear_trend
|
|
6
|
+
from .holt_winters import holt_winters
|
|
7
|
+
from .naive_forecast import naive_forecast
|
|
8
|
+
from .seasonal_naive_forecast import seasonal_naive_forecast
|
|
9
|
+
from .drift_method import drift_method
|
|
10
|
+
from .average_method import average_method
|
|
11
|
+
from .crostons_method import crostons_method
|
|
12
|
+
from .sba_method import sba_method
|
|
13
|
+
from .tsb_method import tsb_method
|
|
14
|
+
from .linear_regression_forecast import linear_regression_forecast
|
|
15
|
+
from .polynomial_regression_forecast import _solve_linear_system
|
|
16
|
+
from .exponential_trend_forecast import exponential_trend_forecast
|
|
17
|
+
from .classical_decomposition import classical_decomposition
|
|
18
|
+
from .moving_median_filter import moving_median_filter
|
|
19
|
+
from .akaike_information_criterion import akaike_information_criterion
|
|
20
|
+
from .bayesian_information_criterion import bayesian_information_criterion
|
|
21
|
+
from .autocorrelation import autocorrelation
|
|
22
|
+
from .partial_autocorrelation import partial_autocorrelation
|
|
23
|
+
from .ar_model import ar_model
|
|
24
|
+
from .ma_model import ma_model
|
|
25
|
+
from .theta_method import theta_method
|
|
26
|
+
from .theils_u import theils_u
|
|
27
|
+
from .tracking_signal import tracking_signal
|
|
28
|
+
from .brier_score import brier_score
|
|
29
|
+
from .browns_double_exponential import browns_double_exponential
|
|
30
|
+
from .browns_triple_exponential import browns_triple_exponential
|
|
31
|
+
from .pegels_classification import pegels_classification
|
|
32
|
+
from .bottom_up_reconciliation import bottom_up_reconciliation
|
|
33
|
+
from .top_down_reconciliation import top_down_reconciliation
|
|
34
|
+
from .bates_granger_combination import bates_granger_combination
|
|
35
|
+
from .dampened_trend import dampened_trend
|
|
36
|
+
from .seasonal_trend_loess import seasonal_trend_loess
|
|
37
|
+
from .box_cox_transform import box_cox_transform
|
|
38
|
+
from .inverse_box_cox import inverse_box_cox
|
|
39
|
+
from .kpss_test import kpss_test
|
|
40
|
+
from .adf_test import adf_test
|
|
41
|
+
from .ljung_box_test import ljung_box_test
|
|
42
|
+
from .hurst_exponent import hurst_exponent
|
|
43
|
+
from .seasonal_indices import seasonal_indices
|
|
44
|
+
from .doubling_seasonal_smoothing import doubling_seasonal_smoothing
|
|
45
|
+
from .gompertz_trend import gompertz_trend
|
|
46
|
+
from .logistic_trend import logistic_trend
|
|
47
|
+
from .mstl_decomposition import mstl_decomposition
|
|
48
|
+
from .rolling_mean_forecast import rolling_mean_forecast
|
|
49
|
+
from .ses_with_drift import ses_with_drift
|
|
50
|
+
from .var_model import var_model
|
|
51
|
+
from .croston_with_decay import croston_with_decay
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Simplified Augmented Dickey-Fuller (ADF) test statistic."""
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def adf_test(data: List[float]) -> float:
|
|
6
|
+
"""Compute simplified ADF t-statistic for unit root testing.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> round(adf_test([1, 2, 3, 4, 5]), 4)
|
|
10
|
+
1.0
|
|
11
|
+
"""
|
|
12
|
+
n = len(data)
|
|
13
|
+
if n < 4:
|
|
14
|
+
raise ValueError("need at least 4 observations")
|
|
15
|
+
# Regress delta(y_t) on y_{t-1} - mean
|
|
16
|
+
mean = sum(data) / n
|
|
17
|
+
dy = [data[t] - data[t - 1] for t in range(1, n)]
|
|
18
|
+
y_lag_centered = [data[t - 1] - mean for t in range(1, n)]
|
|
19
|
+
if all(y == 0 for y in y_lag_centered):
|
|
20
|
+
return 0.0
|
|
21
|
+
num = sum(y_lag_centered[i] * dy[i] for i in range(len(dy)))
|
|
22
|
+
den = sum(y * y for y in y_lag_centered)
|
|
23
|
+
if den == 0:
|
|
24
|
+
return 0.0
|
|
25
|
+
rho = num / den
|
|
26
|
+
residuals = [dy[i] - rho * y_lag_centered[i] for i in range(len(dy))]
|
|
27
|
+
rss = sum(r * r for r in residuals)
|
|
28
|
+
se = math.sqrt(rss / (n - 2)) / math.sqrt(den) if den > 0 and rss > 0 else 0.0
|
|
29
|
+
return rho / se if se > 0 else 0.0
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
import math
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Akaike Information Criterion (AIC)."""
|
|
2
|
+
from typing import List
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def akaike_information_criterion(residuals: List[float], k: int) -> float:
|
|
7
|
+
"""Compute AIC given residuals and number of parameters k.
|
|
8
|
+
|
|
9
|
+
AIC = 2k - 2*ln(L); we use the Gaussian likelihood approximation
|
|
10
|
+
AIC = n * ln(RSS / n) + 2k.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
>>> round(akaike_information_criterion([1.0, -1.0, 0.5, -0.5], 2), 4)
|
|
14
|
+
-3.4657
|
|
15
|
+
"""
|
|
16
|
+
n = len(residuals)
|
|
17
|
+
if n == 0:
|
|
18
|
+
raise ValueError("residuals empty")
|
|
19
|
+
rss = sum(r * r for r in residuals)
|
|
20
|
+
if rss <= 0:
|
|
21
|
+
rss = 1e-12
|
|
22
|
+
return n * math.log(rss / n) + 2 * k
|