Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
H
HYH.APSJ
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
佟礼
HYH.APSJ
Commits
a8a8f6ea
Commit
a8a8f6ea
authored
Sep 23, 2026
by
Tong Li
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MP-调整目标值为最大化,增加kpi 总需求松弛, 增加固定第0层,包含kpi总需求松弛
parent
73c9b8d7
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
117 additions
and
66 deletions
+117
-66
MacroPlannerResultController.java
...java/com/aps/controller/MacroPlannerResultController.java
+1
-1
MacroPlannerOptimizer.java
...main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
+19
-9
KpiAggregator.java
...n/java/com/aps/macroplanner/constraint/KpiAggregator.java
+11
-1
MacroPlannerModel.java
...in/java/com/aps/macroplanner/model/MacroPlannerModel.java
+20
-0
KpiLib.java
src/main/java/com/aps/macroplanner/objective/KpiLib.java
+20
-11
ObjectiveBuilder.java
...java/com/aps/macroplanner/objective/ObjectiveBuilder.java
+5
-5
StrategyLevel.java
...in/java/com/aps/macroplanner/objective/StrategyLevel.java
+9
-9
ResultWriter.java
src/main/java/com/aps/macroplanner/output/ResultWriter.java
+4
-2
SolutionPrinter.java
...ain/java/com/aps/macroplanner/output/SolutionPrinter.java
+25
-25
KpiResult.java
src/main/java/com/aps/macroplanner/output/dto/KpiResult.java
+3
-3
No files found.
src/main/java/com/aps/controller/MacroPlannerResultController.java
View file @
a8a8f6ea
...
@@ -1024,7 +1024,7 @@ public class MacroPlannerResultController {
...
@@ -1024,7 +1024,7 @@ public class MacroPlannerResultController {
entryMap
.
put
(
"name"
,
e
.
name
);
entryMap
.
put
(
"name"
,
e
.
name
);
entryMap
.
put
(
"rawValue"
,
String
.
format
(
"%.2f"
,
e
.
rawValue
));
entryMap
.
put
(
"rawValue"
,
String
.
format
(
"%.2f"
,
e
.
rawValue
));
entryMap
.
put
(
"weight"
,
String
.
format
(
"%.1f"
,
e
.
weight
));
entryMap
.
put
(
"weight"
,
String
.
format
(
"%.1f"
,
e
.
weight
));
entryMap
.
put
(
"
penalty"
,
String
.
format
(
"%.2f"
,
e
.
penalty
));
entryMap
.
put
(
"
contribution"
,
String
.
format
(
"%.2f"
,
e
.
contribution
));
kpiList
.
add
(
entryMap
);
kpiList
.
add
(
entryMap
);
}
}
kpiData
.
put
(
"entries"
,
kpiList
);
kpiData
.
put
(
"entries"
,
kpiList
);
...
...
src/main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
View file @
a8a8f6ea
...
@@ -286,7 +286,7 @@ public class MacroPlannerOptimizer {
...
@@ -286,7 +286,7 @@ public class MacroPlannerOptimizer {
// ========== 1. 前置参数校验 ==========
// ========== 1. 前置参数校验 ==========
List
<
KpiSetting
>
w
=
data
.
getKpiSettings
();
List
<
KpiSetting
>
w
=
data
.
getKpiSettings
();
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
if
(
w
==
null
)
{
if
(
w
==
null
||
w
.
size
()==
0
)
{
levels
=
defineLevels
(
data
.
getKpiWeights
());
levels
=
defineLevels
(
data
.
getKpiWeights
());
}
else
{
}
else
{
levels
=
getKpiLevels
(
w
);
levels
=
getKpiLevels
(
w
);
...
@@ -342,16 +342,18 @@ public class MacroPlannerOptimizer {
...
@@ -342,16 +342,18 @@ public class MacroPlannerOptimizer {
double
bestBound
=
model
.
getSolver
().
objective
().
bestBound
();
double
bestBound
=
model
.
getSolver
().
objective
().
bestBound
();
writeLog
(
" SCIP Status : %s%n"
,
status
);
writeLog
(
" SCIP Status : %s%n"
,
status
);
writeLog
(
" Solving Time (sec) : %.2f%n"
,
levelElapsedMs
/
1000.0
);
writeLog
(
" Solving Time (sec) : %.2f%n"
,
levelElapsedMs
/
1000.0
);
writeLog
(
"
Primal Bound
: %+.6e%n"
,
optimalValue
);
writeLog
(
"
Goal Score
: %+.6e%n"
,
optimalValue
);
writeLog
(
" Best Bound : %+.6e%n"
,
bestBound
);
writeLog
(
" Best Bound : %+.6e%n"
,
bestBound
);
double
gap
=
Math
.
abs
(
optimalValue
-
bestBound
)
/
Math
.
abs
(
optimalValue
)
*
100
;
double
gap
=
Math
.
abs
(
optimalValue
-
bestBound
)
/
Math
.
abs
(
optimalValue
)
*
100
;
writeLog
(
"gap:%.2f"
,
gap
);
writeLog
(
"gap:%.2f"
,
gap
);
// 输出当前层各KPI值
// 输出当前层各KPI值
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
double
kpiValue
=
kpi
.
variable
.
solutionValue
();
double
kpiValue
=
kpi
.
variable
.
solutionValue
();
double
penalty
=
kpi
.
effectiveCoefficient
()
*
kpiValue
;
double
contribution
=
kpi
.
effectiveCoefficient
()
*
kpiValue
;
writeLog
(
" %s: %.2f (系数=%.1f, 惩罚=%.2f)%n"
,
kpi
.
name
,
kpiValue
,
kpi
.
effectiveCoefficient
(),
penalty
);
System
.
out
.
printf
(
" %s: %.2f (系数=%+.1f, 得分贡献=%+.2f)%n"
,
kpi
.
name
,
kpiValue
,
kpi
.
effectiveCoefficient
(),
contribution
);
}
}
levelResults
.
add
(
new
LevelResult
(
level
,
optimalValue
,
status
));
levelResults
.
add
(
new
LevelResult
(
level
,
optimalValue
,
status
));
...
@@ -431,14 +433,19 @@ public class MacroPlannerOptimizer {
...
@@ -431,14 +433,19 @@ public class MacroPlannerOptimizer {
private
List
<
StrategyLevel
>
defineLevels
(
KPIWeights
w
)
{
private
List
<
StrategyLevel
>
defineLevels
(
KPIWeights
w
)
{
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
// === Level 0: 需求满足 (最高优先级, 严格分层, slack=0%) ===
StrategyLevel
l0
=
new
StrategyLevel
(
0
,
"需求松弛"
,
0.0
);
l0
.
addKPI
(
"TotalSlack"
,
"需求松弛"
,
model
.
getTotalSlack
(),
1
);
levels
.
add
(
l0
);
// === Level 1: 需求满足 (最高优先级, 严格分层, slack=0%) ===
// === Level 1: 需求满足 (最高优先级, 严格分层, slack=0%) ===
StrategyLevel
l1
=
new
StrategyLevel
(
1
,
"需求满足"
,
0.0
);
StrategyLevel
l1
=
new
StrategyLevel
(
1
,
"需求满足"
,
0.0
);
l1
.
addKPI
(
KpiLib
.
Fulfillment
.
getEn
(),
KpiLib
.
Fulfillment
.
getCn
(),
model
.
getTotalFulfillment
(),
w
.
getFulfillmentWeight
());
l1
.
addKPI
(
KpiLib
.
Fulfillment
.
getEn
(),
KpiLib
.
Fulfillment
.
getCn
(),
model
.
getTotalFulfillment
(),
w
.
getFulfillmentWeight
()
,
true
);
levels
.
add
(
l1
);
levels
.
add
(
l1
);
// === Level 2: 产能约束 (物理硬约束, 严格分层, slack=0%) ===
// === Level 2: 产能约束 (物理硬约束, 严格分层, slack=0%) ===
StrategyLevel
l2
=
new
StrategyLevel
(
2
,
"产能约束"
,
0.0
);
StrategyLevel
l2
=
new
StrategyLevel
(
2
,
"产能约束"
,
0.0
);
l2
.
addKPI
(
KpiLib
.
UnitCapacity
.
getEn
(),
KpiLib
.
Fulfillment
.
getCn
(),
model
.
getTotalUnitCapacity
(),
w
.
getUnitCapacityWeight
());
l2
.
addKPI
(
KpiLib
.
UnitCapacity
.
getEn
(),
KpiLib
.
UnitCapacity
.
getCn
(),
model
.
getTotalUnitCapacity
(),
w
.
getUnitCapacityWeight
());
levels
.
add
(
l2
);
levels
.
add
(
l2
);
// === Level 3: 业务KPI (允许 5% 退化, slack=5%) ===
// === Level 3: 业务KPI (允许 5% 退化, slack=5%) ===
...
@@ -472,7 +479,10 @@ public class MacroPlannerOptimizer {
...
@@ -472,7 +479,10 @@ public class MacroPlannerOptimizer {
TreeMap:
:
new
,
TreeMap:
:
new
,
Collectors
.
toList
()
Collectors
.
toList
()
));
));
// === Level 0: 需求满足 (最高优先级, 严格分层, slack=0%) ===
StrategyLevel
l0
=
new
StrategyLevel
(
0
,
"需求松弛"
,
0.0
);
l0
.
addKPI
(
"TotalSlack"
,
"需求松弛"
,
model
.
getTotalSlack
(),
1
);
levels
.
add
(
l0
);
for
(
Map
.
Entry
<
Integer
,
List
<
KpiSetting
>>
entry
:
groupByLevelMap
.
entrySet
())
{
for
(
Map
.
Entry
<
Integer
,
List
<
KpiSetting
>>
entry
:
groupByLevelMap
.
entrySet
())
{
Integer
currentLevel
=
entry
.
getKey
();
Integer
currentLevel
=
entry
.
getKey
();
List
<
KpiSetting
>
kpiGroup
=
entry
.
getValue
();
List
<
KpiSetting
>
kpiGroup
=
entry
.
getValue
();
...
@@ -480,7 +490,7 @@ public class MacroPlannerOptimizer {
...
@@ -480,7 +490,7 @@ public class MacroPlannerOptimizer {
// 遍历当前分组里面每一个KPI
// 遍历当前分组里面每一个KPI
for
(
KpiSetting
kpi
:
kpiGroup
)
{
for
(
KpiSetting
kpi
:
kpiGroup
)
{
level
.
addKPI
(
kpi
.
code
,
kpi
.
name
,
model
.
getKpi
(
kpi
.
code
),
kpi
.
weight
);
level
.
addKPI
(
kpi
.
code
,
kpi
.
name
,
model
.
getKpi
(
kpi
.
code
),
kpi
.
weight
,
KpiLib
.
ofEn
(
kpi
.
code
).
getIsBenefit
()
);
}
}
...
...
src/main/java/com/aps/macroplanner/constraint/KpiAggregator.java
View file @
a8a8f6ea
...
@@ -38,8 +38,12 @@ public class KpiAggregator {
...
@@ -38,8 +38,12 @@ public class KpiAggregator {
public
static
void
build
(
MacroPlannerModel
model
,
TestDataBuilder
data
)
{
public
static
void
build
(
MacroPlannerModel
model
,
TestDataBuilder
data
)
{
double
inf
=
MPSolver
.
infinity
();
double
inf
=
MPSolver
.
infinity
();
// TotalFulfillment = Σ
DemandSlack
// TotalFulfillment = Σ
SalesDemandQty (Quintiq fulfillment bonus KPI)
model
.
setTotalFulfillment
(
createSumKpi
(
model
,
"TotalFulfillment"
,
model
.
setTotalFulfillment
(
createSumKpi
(
model
,
"TotalFulfillment"
,
model
.
getSalesDemandQtyVars
()));
// TotalSlack = Σ DemandSlack (Quintiq Slack KPI, 高优先级惩罚)
model
.
setTotalSlack
(
createSumKpi
(
model
,
"TotalSlack"
,
model
.
getDemandSlackVars
()));
model
.
getDemandSlackVars
()));
// TotalLotSize = Σ PTLotSizeOver + Σ PTLotSizeUnder
// TotalLotSize = Σ PTLotSizeOver + Σ PTLotSizeUnder
...
@@ -66,10 +70,16 @@ public class KpiAggregator {
...
@@ -66,10 +70,16 @@ public class KpiAggregator {
model
.
setTotalUnitCapacity
(
createSumKpi
(
model
,
"TotalUnitCapacity"
,
model
.
setTotalUnitCapacity
(
createSumKpi
(
model
,
"TotalUnitCapacity"
,
model
.
getCapacityOverloadedVars
()));
model
.
getCapacityOverloadedVars
()));
// TotalMinimumUnitCapacity = Σ CapacityNotMet (Quintiq 最小产能未满足 KPI)
model
.
setTotalMinimumUnitCapacity
(
createSumKpi
(
model
,
"TotalMinUnitCapacity"
,
model
.
getCapacityNotMetVars
()));
// TotalSupplyTarget = Σ SupplyTargetQtyUnder
// TotalSupplyTarget = Σ SupplyTargetQtyUnder
model
.
setTotalSupplyTarget
(
createSumKpi
(
model
,
"TotalSupplyTarget"
,
model
.
setTotalSupplyTarget
(
createSumKpi
(
model
,
"TotalSupplyTarget"
,
model
.
getSupplyTargetQtyUnderVars
()));
model
.
getSupplyTargetQtyUnderVars
()));
// TotalMinSupply = Σ MinSupplyQtyUnder
// TotalMinSupply = Σ MinSupplyQtyUnder
model
.
setTotalMinSupply
(
createSumKpi
(
model
,
"TotalMinSupply"
,
model
.
setTotalMinSupply
(
createSumKpi
(
model
,
"TotalMinSupply"
,
model
.
getMinSupplyQtyUnderVars
()));
model
.
getMinSupplyQtyUnderVars
()));
...
...
src/main/java/com/aps/macroplanner/model/MacroPlannerModel.java
View file @
a8a8f6ea
...
@@ -80,12 +80,19 @@ public class MacroPlannerModel {
...
@@ -80,12 +80,19 @@ public class MacroPlannerModel {
private
final
Map
<
String
,
MPVariable
>
ptLotSizeUnderVars
=
new
HashMap
<>();
private
final
Map
<
String
,
MPVariable
>
ptLotSizeUnderVars
=
new
HashMap
<>();
// ==================== KPI 汇总变量 ====================
// ==================== KPI 汇总变量 ====================
private
MPVariable
totalSlack
;
private
MPVariable
totalFulfillment
;
private
MPVariable
totalFulfillment
;
private
MPVariable
totalLotSize
;
private
MPVariable
totalLotSize
;
private
MPVariable
totalMaxInventoryLevel
;
private
MPVariable
totalMaxInventoryLevel
;
private
MPVariable
totalMinInventoryLevel
;
private
MPVariable
totalMinInventoryLevel
;
private
MPVariable
totalTargetInvLevel
;
private
MPVariable
totalTargetInvLevel
;
private
MPVariable
totalUnitCapacity
;
private
MPVariable
totalUnitCapacity
;
private
MPVariable
totalMinimumUnitCapacity
;
private
MPVariable
totalSupplyTarget
;
private
MPVariable
totalSupplyTarget
;
private
MPVariable
totalMinSupply
;
private
MPVariable
totalMinSupply
;
private
MPVariable
totalMaxSupply
;
private
MPVariable
totalMaxSupply
;
...
@@ -123,6 +130,7 @@ public class MacroPlannerModel {
...
@@ -123,6 +130,7 @@ public class MacroPlannerModel {
}
}
// ==================== KPI 变量 setter (由 KpiAggregator 调用) ====================
// ==================== KPI 变量 setter (由 KpiAggregator 调用) ====================
public
void
setTotalSlack
(
MPVariable
v
)
{
this
.
totalSlack
=
v
;
}
public
void
setTotalFulfillment
(
MPVariable
v
)
{
this
.
totalFulfillment
=
v
;
}
public
void
setTotalFulfillment
(
MPVariable
v
)
{
this
.
totalFulfillment
=
v
;
}
public
void
setTotalLotSize
(
MPVariable
v
)
{
this
.
totalLotSize
=
v
;
}
public
void
setTotalLotSize
(
MPVariable
v
)
{
this
.
totalLotSize
=
v
;
}
...
@@ -130,6 +138,9 @@ public class MacroPlannerModel {
...
@@ -130,6 +138,9 @@ public class MacroPlannerModel {
public
void
setTotalMinInventoryLevel
(
MPVariable
v
)
{
this
.
totalMinInventoryLevel
=
v
;
}
public
void
setTotalMinInventoryLevel
(
MPVariable
v
)
{
this
.
totalMinInventoryLevel
=
v
;
}
public
void
setTotalTargetInvLevel
(
MPVariable
v
)
{
this
.
totalTargetInvLevel
=
v
;
}
public
void
setTotalTargetInvLevel
(
MPVariable
v
)
{
this
.
totalTargetInvLevel
=
v
;
}
public
void
setTotalUnitCapacity
(
MPVariable
v
)
{
this
.
totalUnitCapacity
=
v
;
}
public
void
setTotalUnitCapacity
(
MPVariable
v
)
{
this
.
totalUnitCapacity
=
v
;
}
public
void
setTotalMinimumUnitCapacity
(
MPVariable
v
)
{
this
.
totalMinimumUnitCapacity
=
v
;
}
public
void
setTotalSupplyTarget
(
MPVariable
v
)
{
this
.
totalSupplyTarget
=
v
;
}
public
void
setTotalSupplyTarget
(
MPVariable
v
)
{
this
.
totalSupplyTarget
=
v
;
}
public
void
setTotalMinSupply
(
MPVariable
v
)
{
this
.
totalMinSupply
=
v
;
}
public
void
setTotalMinSupply
(
MPVariable
v
)
{
this
.
totalMinSupply
=
v
;
}
public
void
setTotalMaxSupply
(
MPVariable
v
)
{
this
.
totalMaxSupply
=
v
;
}
public
void
setTotalMaxSupply
(
MPVariable
v
)
{
this
.
totalMaxSupply
=
v
;
}
...
@@ -150,6 +161,8 @@ public class MacroPlannerModel {
...
@@ -150,6 +161,8 @@ public class MacroPlannerModel {
public
Map
<
String
,
MPVariable
>
getDemandFulfillmentVars
()
{
return
demandFulfillmentVars
;
}
public
Map
<
String
,
MPVariable
>
getDemandFulfillmentVars
()
{
return
demandFulfillmentVars
;
}
public
Map
<
String
,
MPVariable
>
getCapacityOverloadedVars
()
{
return
capacityOverloadedVars
;
}
public
Map
<
String
,
MPVariable
>
getCapacityOverloadedVars
()
{
return
capacityOverloadedVars
;
}
public
Map
<
String
,
MPVariable
>
getCapacityNotMetVars
()
{
return
capacityNotMetVars
;
}
public
Map
<
String
,
MPVariable
>
getCapacityNotMetVars
()
{
return
capacityNotMetVars
;
}
public
Map
<
String
,
MPVariable
>
getMinInvQtyUnderVars
()
{
return
minInvQtyUnderVars
;
}
public
Map
<
String
,
MPVariable
>
getMinInvQtyUnderVars
()
{
return
minInvQtyUnderVars
;
}
public
Map
<
String
,
MPVariable
>
getMaxInvQtyOverVars
()
{
return
maxInvQtyOverVars
;
}
public
Map
<
String
,
MPVariable
>
getMaxInvQtyOverVars
()
{
return
maxInvQtyOverVars
;
}
public
Map
<
String
,
MPVariable
>
getInvQtyUnderTargetVars
()
{
return
invQtyUnderTargetVars
;
}
public
Map
<
String
,
MPVariable
>
getInvQtyUnderTargetVars
()
{
return
invQtyUnderTargetVars
;
}
...
@@ -159,12 +172,16 @@ public class MacroPlannerModel {
...
@@ -159,12 +172,16 @@ public class MacroPlannerModel {
public
Map
<
String
,
MPVariable
>
getPtLotSizeOverVars
()
{
return
ptLotSizeOverVars
;
}
public
Map
<
String
,
MPVariable
>
getPtLotSizeOverVars
()
{
return
ptLotSizeOverVars
;
}
public
Map
<
String
,
MPVariable
>
getPtLotSizeUnderVars
()
{
return
ptLotSizeUnderVars
;
}
public
Map
<
String
,
MPVariable
>
getPtLotSizeUnderVars
()
{
return
ptLotSizeUnderVars
;
}
public
MPVariable
getTotalSlack
()
{
return
totalSlack
;
}
public
MPVariable
getTotalFulfillment
()
{
return
totalFulfillment
;
}
public
MPVariable
getTotalFulfillment
()
{
return
totalFulfillment
;
}
public
MPVariable
getTotalLotSize
()
{
return
totalLotSize
;
}
public
MPVariable
getTotalLotSize
()
{
return
totalLotSize
;
}
public
MPVariable
getTotalMaxInventoryLevel
()
{
return
totalMaxInventoryLevel
;
}
public
MPVariable
getTotalMaxInventoryLevel
()
{
return
totalMaxInventoryLevel
;
}
public
MPVariable
getTotalMinInventoryLevel
()
{
return
totalMinInventoryLevel
;
}
public
MPVariable
getTotalMinInventoryLevel
()
{
return
totalMinInventoryLevel
;
}
public
MPVariable
getTotalTargetInvLevel
()
{
return
totalTargetInvLevel
;
}
public
MPVariable
getTotalTargetInvLevel
()
{
return
totalTargetInvLevel
;
}
public
MPVariable
getTotalUnitCapacity
()
{
return
totalUnitCapacity
;
}
public
MPVariable
getTotalUnitCapacity
()
{
return
totalUnitCapacity
;
}
public
MPVariable
getTotalMinimumUnitCapacity
()
{
return
totalMinimumUnitCapacity
;
}
public
MPVariable
getTotalSupplyTarget
()
{
return
totalSupplyTarget
;
}
public
MPVariable
getTotalSupplyTarget
()
{
return
totalSupplyTarget
;
}
public
MPVariable
getTotalMinSupply
()
{
return
totalMinSupply
;
}
public
MPVariable
getTotalMinSupply
()
{
return
totalMinSupply
;
}
public
MPVariable
getTotalMaxSupply
()
{
return
totalMaxSupply
;
}
public
MPVariable
getTotalMaxSupply
()
{
return
totalMaxSupply
;
}
...
@@ -183,6 +200,9 @@ public class MacroPlannerModel {
...
@@ -183,6 +200,9 @@ public class MacroPlannerModel {
case
UnitCapacity:
case
UnitCapacity:
kpivar
=
totalUnitCapacity
;
kpivar
=
totalUnitCapacity
;
break
;
break
;
case
MinUnitCapacity:
kpivar
=
totalMinimumUnitCapacity
;
break
;
case
LotSize:
case
LotSize:
kpivar
=
totalLotSize
;
kpivar
=
totalLotSize
;
break
;
break
;
...
...
src/main/java/com/aps/macroplanner/objective/KpiLib.java
View file @
a8a8f6ea
...
@@ -9,23 +9,28 @@ import java.util.stream.Collectors;
...
@@ -9,23 +9,28 @@ import java.util.stream.Collectors;
* 时间:2026-09-22
* 时间:2026-09-22
*/
*/
public
enum
KpiLib
{
public
enum
KpiLib
{
Fulfillment
(
"需求满足"
),
Fulfillment
(
"需求满足"
,
true
),
UnitCapacity
(
"产能"
),
UnitCapacity
(
"产能"
,
false
),
LotSize
(
"批次"
),
MinUnitCapacity
(
"最小化产能"
,
false
),
TargetInvLevel
(
"目标库存"
),
LotSize
(
"批次"
,
false
),
SupplyTarget
(
"供应目标"
),
TargetInvLevel
(
"目标库存"
,
false
),
SalesDemandPriority
(
"销售优先级"
),
SupplyTarget
(
"供应目标"
,
false
),
MaxInventoryLevel
(
"最大库存"
),
SalesDemandPriority
(
"销售优先级"
,
false
),
MinInventoryLevel
(
"最小库存"
),
MaxInventoryLevel
(
"最大库存"
,
false
),
MinSupply
(
"最小供应"
),
MinInventoryLevel
(
"最小库存"
,
false
),
MaxSupply
(
"最大供应"
);
MinSupply
(
"最小供应"
,
false
),
MaxSupply
(
"最大供应"
,
false
);
private
final
String
en
;
private
final
String
en
;
private
final
String
cn
;
private
final
String
cn
;
KpiLib
(
String
cn
)
{
/** 是否为收益 KPI(越大越好)。 */
public
final
boolean
isBenefit
;
KpiLib
(
String
cn
,
boolean
isBenefit
)
{
this
.
en
=
this
.
name
();
this
.
en
=
this
.
name
();
this
.
cn
=
cn
;
this
.
cn
=
cn
;
this
.
isBenefit
=
isBenefit
;
}
}
public
String
getEn
()
{
public
String
getEn
()
{
...
@@ -36,6 +41,10 @@ public enum KpiLib {
...
@@ -36,6 +41,10 @@ public enum KpiLib {
return
cn
;
return
cn
;
}
}
public
boolean
getIsBenefit
()
{
return
isBenefit
;
}
/**
/**
* 转为 Map<英文编码,中文名称>,用于批量翻译、表头转换
* 转为 Map<英文编码,中文名称>,用于批量翻译、表头转换
*/
*/
...
...
src/main/java/com/aps/macroplanner/objective/ObjectiveBuilder.java
View file @
a8a8f6ea
...
@@ -168,18 +168,18 @@ public class ObjectiveBuilder {
...
@@ -168,18 +168,18 @@ public class ObjectiveBuilder {
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
objective
.
setCoefficient
(
kpi
.
variable
,
kpi
.
effectiveCoefficient
());
objective
.
setCoefficient
(
kpi
.
variable
,
kpi
.
effectiveCoefficient
());
}
}
objective
.
setM
in
imization
();
objective
.
setM
ax
imization
();
}
}
/**
/**
* 添加层级边界约束 — 限制上层
目标值不超过最优值 × (1 + slack)
。
* 添加层级边界约束 — 限制上层
得分不低于最优值减去允许松弛
。
*
*
* <p>该约束确保在求解下层 KPI 时, 上层 KPI 不会退化超过允许范围。
* <p>该约束确保在求解下层 KPI 时, 上层 KPI 不会退化超过允许范围。
* 对应 Quintiq 中 StrategyLevel 的 HierarchicalSolver 约束。</p>
* 对应 Quintiq 中 StrategyLevel 的 HierarchicalSolver 约束。</p>
*
*
* <h3>数学公式</h3>
* <h3>数学公式</h3>
* <pre>
* <pre>
* Σ (effectiveCoeff × KPI_variable)
≤ optimalValue + |optimalValue| × relativeGoalSlack
* Σ (effectiveCoeff × KPI_variable)
≥ optimalValue - |optimalValue × relativeGoalSlack|
* </pre>
* </pre>
*
*
* @param model 模型容器
* @param model 模型容器
...
@@ -192,10 +192,10 @@ public class ObjectiveBuilder {
...
@@ -192,10 +192,10 @@ public class ObjectiveBuilder {
if
(
level
.
getRelativeGoalSlack
()
<
0.0
)
return
;
// 负松弛表示不约束
if
(
level
.
getRelativeGoalSlack
()
<
0.0
)
return
;
// 负松弛表示不约束
MPSolver
solver
=
model
.
getSolver
();
MPSolver
solver
=
model
.
getSolver
();
double
upperBound
=
computeUpperBound
(
optimalValue
,
level
.
getRelativeGoalSlack
());
double
lowerBound
=
optimalValue
-
Math
.
abs
(
optimalValue
*
level
.
getRelativeGoalSlack
());
MPConstraint
bound
=
solver
.
makeConstraint
(
MPConstraint
bound
=
solver
.
makeConstraint
(
-
MPSolver
.
infinity
(),
upperBound
,
lowerBound
,
MPSolver
.
infinity
()
,
"HierLevel"
+
level
.
getLevel
()
+
"_Bound"
);
"HierLevel"
+
level
.
getLevel
()
+
"_Bound"
);
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
...
...
src/main/java/com/aps/macroplanner/objective/StrategyLevel.java
View file @
a8a8f6ea
...
@@ -60,8 +60,8 @@ public class StrategyLevel {
...
@@ -60,8 +60,8 @@ public class StrategyLevel {
/** 该 KPI 在当前层级内的权重 */
/** 该 KPI 在当前层级内的权重 */
public
final
double
weight
;
public
final
double
weight
;
/** 是否为
负向 KPI (越小越好 = 正常惩罚项; false = 正常惩罚项)
*/
/** 是否为
收益 KPI(越大越好)。
*/
public
final
boolean
is
Negative
;
public
final
boolean
is
Benefit
;
/** 编号 */
/** 编号 */
public
final
String
code
;
public
final
String
code
;
...
@@ -69,20 +69,20 @@ public class StrategyLevel {
...
@@ -69,20 +69,20 @@ public class StrategyLevel {
/** KPI 名称 (用于日志) */
/** KPI 名称 (用于日志) */
public
final
String
name
;
public
final
String
name
;
public
KPIEntry
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
is
Negative
)
{
public
KPIEntry
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
is
Benefit
)
{
this
.
code
=
code
;
this
.
code
=
code
;
this
.
name
=
name
;
this
.
name
=
name
;
this
.
variable
=
variable
;
this
.
variable
=
variable
;
this
.
weight
=
weight
;
this
.
weight
=
weight
;
this
.
is
Negative
=
isNegative
;
this
.
is
Benefit
=
isBenefit
;
}
}
/**
/**
* 计算该 KPI 在目标函数中的实际系数。
* 计算该 KPI 在目标函数中的实际系数。
*
负向 KPI (如 SalesDemandPriority) 使用负系数实现最大化
。
*
收益 KPI 使用正系数;惩罚 KPI 使用负系数
。
*/
*/
public
double
effectiveCoefficient
()
{
public
double
effectiveCoefficient
()
{
return
is
Negative
?
-
weight
:
weight
;
return
is
Benefit
?
weight
:
-
weight
;
}
}
}
}
...
@@ -116,11 +116,11 @@ public class StrategyLevel {
...
@@ -116,11 +116,11 @@ public class StrategyLevel {
* @param name KPI 名称
* @param name KPI 名称
* @param variable KPI 汇总变量
* @param variable KPI 汇总变量
* @param weight 权重 (0 = 跳过)
* @param weight 权重 (0 = 跳过)
* @param is
Negative 是否为负向 KPI (true = 最大化, 使用负系数)
* @param is
Benefit 是否为收益 KPI(true = 最大化得分中的正系数)
*/
*/
public
void
addKPI
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
isNegative
)
{
public
void
addKPI
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
isBenefit
)
{
if
(
weight
>
0.0
&&
variable
!=
null
)
{
if
(
weight
>
0.0
&&
variable
!=
null
)
{
kpis
.
add
(
new
KPIEntry
(
code
,
name
,
variable
,
weight
,
is
Negative
));
kpis
.
add
(
new
KPIEntry
(
code
,
name
,
variable
,
weight
,
is
Benefit
));
}
}
}
}
...
...
src/main/java/com/aps/macroplanner/output/ResultWriter.java
View file @
a8a8f6ea
...
@@ -535,7 +535,7 @@ public class ResultWriter {
...
@@ -535,7 +535,7 @@ public class ResultWriter {
// KPI + 统计
// KPI + 统计
writeLog
(
"Kpi"
);
writeLog
(
"Kpi"
);
//
result.setKpis(buildKpis());
result
.
setKpis
(
buildKpis
());
// result.setStatistics(buildStatistics());
// result.setStatistics(buildStatistics());
return
result
;
return
result
;
...
@@ -1491,6 +1491,8 @@ public class ResultWriter {
...
@@ -1491,6 +1491,8 @@ public class ResultWriter {
double
objValue
=
model
.
getSolver
().
objective
().
value
();
double
objValue
=
model
.
getSolver
().
objective
().
value
();
kr
.
setObjectiveValue
(
objValue
);
kr
.
setObjectiveValue
(
objValue
);
kr
.
addEntry
(
"需求缺口(Fulfillment)"
,
kr
.
addEntry
(
"需求缺口(Fulfillment)"
,
model
.
getTotalFulfillment
().
solutionValue
(),
model
.
getTotalFulfillment
().
solutionValue
(),
w
.
getFulfillmentWeight
(),
false
);
w
.
getFulfillmentWeight
(),
false
);
...
@@ -1715,7 +1717,7 @@ public class ResultWriter {
...
@@ -1715,7 +1717,7 @@ public class ResultWriter {
.
key
(
"name"
).
val
(
e
.
name
)
.
key
(
"name"
).
val
(
e
.
name
)
.
key
(
"rawValue"
).
val
(
e
.
rawValue
)
.
key
(
"rawValue"
).
val
(
e
.
rawValue
)
.
key
(
"weight"
).
val
(
e
.
weight
)
.
key
(
"weight"
).
val
(
e
.
weight
)
.
key
(
"
penalty"
).
val
(
e
.
penalty
)
.
key
(
"
contribution"
).
val
(
e
.
contribution
)
.
key
(
"isBenefit"
).
val
(
e
.
isBenefit
)
.
key
(
"isBenefit"
).
val
(
e
.
isBenefit
)
.
endObj
();
.
endObj
();
}
}
...
...
src/main/java/com/aps/macroplanner/output/SolutionPrinter.java
View file @
a8a8f6ea
...
@@ -693,37 +693,37 @@ public class SolutionPrinter {
...
@@ -693,37 +693,37 @@ public class SolutionPrinter {
// 计算并输出加权总惩罚 (等价于单目标函数值)
// 计算并输出加权总惩罚 (等价于单目标函数值)
writeLog
(
""
);
writeLog
(
""
);
writeLog
(
"--- 最终 KPI 值 (加权总惩罚) ---"
);
//
writeLog("--- 最终 KPI 值 (加权总惩罚) ---");
double
fulfillment
=
model
.
getTotalFulfillment
().
solutionValue
();
//
double fulfillment = model.getTotalFulfillment().solutionValue();
double
lotSize
=
model
.
getTotalLotSize
().
solutionValue
();
//
double lotSize = model.getTotalLotSize().solutionValue();
double
maxInv
=
model
.
getTotalMaxInventoryLevel
().
solutionValue
();
//
double maxInv = model.getTotalMaxInventoryLevel().solutionValue();
double
minInv
=
model
.
getTotalMinInventoryLevel
().
solutionValue
();
//
double minInv = model.getTotalMinInventoryLevel().solutionValue();
double
targetInv
=
model
.
getTotalTargetInvLevel
().
solutionValue
();
//
double targetInv = model.getTotalTargetInvLevel().solutionValue();
double
capacity
=
model
.
getTotalUnitCapacity
().
solutionValue
();
//
double capacity = model.getTotalUnitCapacity().solutionValue();
double
supplyTarget
=
model
.
getTotalSupplyTarget
().
solutionValue
();
//
double supplyTarget = model.getTotalSupplyTarget().solutionValue();
double
minSupply
=
model
.
getTotalMinSupply
().
solutionValue
();
//
double minSupply = model.getTotalMinSupply().solutionValue();
double
maxSupply
=
model
.
getTotalMaxSupply
().
solutionValue
();
//
double maxSupply = model.getTotalMaxSupply().solutionValue();
double
salesPriority
=
model
.
getTotalSalesDemandPriority
().
solutionValue
();
//
double salesPriority = model.getTotalSalesDemandPriority().solutionValue();
double
totalPenalty
=
fulfillment
*
weights
.
getOrDefault
(
KpiLib
.
Fulfillment
.
getEn
(),
0
d
)
//
double totalPenalty = fulfillment * weights.getOrDefault(KpiLib.Fulfillment.getEn(),0d)
+
lotSize
*
weights
.
getOrDefault
(
KpiLib
.
LotSize
.
getEn
(),
0
d
)
//
+ lotSize * weights.getOrDefault(KpiLib.LotSize.getEn(),0d)
+
maxInv
*
weights
.
getOrDefault
(
KpiLib
.
MinInventoryLevel
.
getEn
(),
0
d
)
//
+ maxInv * weights.getOrDefault(KpiLib.MinInventoryLevel.getEn(),0d)
+
minInv
*
weights
.
getOrDefault
(
KpiLib
.
MinInventoryLevel
.
getEn
(),
0
d
)
//
+ minInv * weights.getOrDefault(KpiLib.MinInventoryLevel.getEn(),0d)
+
targetInv
*
weights
.
getOrDefault
(
KpiLib
.
TargetInvLevel
.
getEn
(),
0
d
)
//
+ targetInv * weights.getOrDefault(KpiLib.TargetInvLevel.getEn(),0d)
+
capacity
*
weights
.
getOrDefault
(
KpiLib
.
UnitCapacity
.
getEn
(),
0
d
)
//
+ capacity * weights.getOrDefault(KpiLib.UnitCapacity.getEn(),0d)
+
supplyTarget
*
weights
.
getOrDefault
(
KpiLib
.
SupplyTarget
.
getEn
(),
0
d
)
//
+ supplyTarget * weights.getOrDefault(KpiLib.SupplyTarget.getEn(),0d)
+
minSupply
*
weights
.
getOrDefault
(
KpiLib
.
MinSupply
.
getEn
(),
0
d
)
//
+ minSupply * weights.getOrDefault(KpiLib.MinSupply.getEn(),0d)
+
maxSupply
*
weights
.
getOrDefault
(
KpiLib
.
MaxSupply
.
getEn
(),
0
d
)
//
+ maxSupply * weights.getOrDefault(KpiLib.MaxSupply.getEn(),0d)
-
salesPriority
*
weights
.
getOrDefault
(
KpiLib
.
SalesDemandPriority
.
getEn
(),
0
d
);
//
- salesPriority * weights.getOrDefault(KpiLib.SalesDemandPriority.getEn(),0d);
//
writeLog
(
" 加权总惩罚
: %.2f"
,
totalPenalty
);
// writeLog(" 总kpi
: %.2f", totalPenalty);
for
(
Map
.
Entry
<
String
,
Double
>
entry
:
weights
.
entrySet
())
{
for
(
Map
.
Entry
<
String
,
Double
>
entry
:
weights
.
entrySet
())
{
String
key
=
entry
.
getKey
();
String
key
=
entry
.
getKey
();
Double
weight
=
entry
.
getValue
();
Double
weight
=
entry
.
getValue
();
KpiLib
kpi
=
KpiLib
.
ofEn
(
key
);
KpiLib
kpi
=
KpiLib
.
ofEn
(
key
);
double
val
=
model
.
getKpi
(
key
).
solutionValue
();
double
val
=
model
.
getKpi
(
key
).
solutionValue
();
writeLog
(
" %s: %.2f (
权重
%.0f × %.2f)"
,
writeLog
(
" %s: %.2f (%.0f × %.2f)"
,
kpi
.
getCn
(),
val
*
weight
,
val
,
weight
);
kpi
.
getCn
(),
val
*
weight
,
val
,
weight
);
}
}
...
...
src/main/java/com/aps/macroplanner/output/dto/KpiResult.java
View file @
a8a8f6ea
...
@@ -24,8 +24,8 @@ public class KpiResult {
...
@@ -24,8 +24,8 @@ public class KpiResult {
public
double
rawValue
;
public
double
rawValue
;
/** 权重 */
/** 权重 */
public
double
weight
;
public
double
weight
;
/**
加权惩罚 = rawValue × weight
*/
/**
得分贡献 = rawValue × weight(收益项为正, 惩罚项为负)
*/
public
double
penalty
;
public
double
contribution
;
/** 是否为收益项 (越大越好, 正系数) */
/** 是否为收益项 (越大越好, 正系数) */
public
boolean
isBenefit
;
public
boolean
isBenefit
;
}
}
...
@@ -43,7 +43,7 @@ public class KpiResult {
...
@@ -43,7 +43,7 @@ public class KpiResult {
e
.
rawValue
=
rawValue
;
e
.
rawValue
=
rawValue
;
e
.
weight
=
weight
;
e
.
weight
=
weight
;
e
.
isBenefit
=
isBenefit
;
e
.
isBenefit
=
isBenefit
;
e
.
penalty
=
isBenefit
?
-
rawValue
*
weight
:
rawValue
*
weight
;
e
.
contribution
=
isBenefit
?
rawValue
*
weight
:
-
rawValue
*
weight
;
entries
.
add
(
e
);
entries
.
add
(
e
);
}
}
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment