什么是最小的可再生的例子(绝笔)?
绝笔是一个代码示例:
- 是尽可能的小
- 包含所有数据、变量、函数等需要重现的问题(但没有更多)
- 一直测试由你来确保它是可再生的
最后重点意味着你应该运行在一个新文件(或与一个新内核如果你使用Python Jupyter笔记本)在发布之前在社区论坛。
使用代码块。
明确你的问题(尝试以粗体突出一个句子)。
不粘贴代码的屏幕截图。
不粘贴的代码不正确格式化的(如果Python和缩进)。
为什么我们需要一个最小可再生的例子吗?
回答你的问题在社区论坛的人经常这样做自己的时间——这包括Gurobi支持人员。你的时间是宝贵的,但是他们也是如此。你应该让你的问题尽可能容易让别人去理解和解决。
如果人们看到你努力让你的最小的和可再生的例子,那么你更有可能得到一个更快、更彻底的反应。
绝笔会使问题更容易识别的原因。如果你的例子是无法复制的,那么大量的时间可能与回复来回,直到提供了足够的信息让它重现。
让我们看看它是如何完成了!
例如,下面的代码。
从gurobipy进口*
进口numpy np
从gurobipy导入模型、LinExpr QuadExpr, quicksum伽马线暴
出现从itertools进口组合
#数据
def make_data ():
ε= 1 e-6
#输入数据
k = 5
x = np。阵列([[1,1,1,1,- 1),
(1,1,1,1,- 1),
(1,1,1,1,1]])
m = 3
返回ε,x, k、m
def make_model(数据):
ε,x, k、m =数据
#空模型进行初始化
模型=模型(“模式”)
模型。setParam(“凸”,2)
模型。setParam (“MIPGap”, 0.01)
#添加变量
var_w = model.addVars(范围(k),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
var_cos = model.addVars(组合(范围(k), 2),磅= 1,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
ox_1 = model.addVars(范围(m),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
ox_2 = model.addVars(范围(m),组合(范围(k), 2),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
sox_2 = model.addVars(范围(m),组合(范围(k), 2),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
sox_3 = model.addVars(范围(m),组合(范围(k), 2),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
# x正常化
x_normed = x / x.sum(轴= 0)
我的范围(米):
模型。addConstr (ox_1[我]= =总和(var_w [h] * x_normed[我][h]的h范围(k)))
我的范围(米):
h、l组合(范围(k), 2):
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
模型。addGenConstrPow (ox_2[我][h] [l], sox_2[我][h] [l], 0.5,“女朋友”,“FuncPieces = 1000”)
模型。addConstr (sox_3[我][h] [l] = = sox_2[我][h] [l] * var_cos [h] [l])
我的范围(米):
ox_3 = 0
h、l组合(范围(k), 2):
ox_3 + = 2 * soc_3[我][h] [l]
回归模型
def main ():
data = make_data ()
模型= make_model(数据)
model.optimize ()
如果model.getAttr(“状态”)= = GRB.OPTIMAL:
在model.getVars var ():
如果var.getAttr (x) ! = 0:
print (var.getAttr (“VarName”), var.getAttr (" X "))
打印(“\ n达到最佳:{}“.format (model.getObjective () .getValue ()))
其他:
打印(“\ n也许不可行模式。”)
if __name__ = =“__main__”:
main ()
当我们运行这段代码时,我们得到的错误:“KeyError: 0”
让我们创建一个社区的帖子!我们的代码已经重现——如果你复制粘贴到你自己的机器上你会得到相同的结果作为其他人(假定导入语句成功)。但这是一个大量代码——这是最小的。
现在,在社区提供简短的错误消息后比简单地说“我有一个错误”,但我们可以做得更好。当构建一个绝笔,我们想提供尽可能多的有用的信息。如果没有有用的信息然后是无益的!如果我们有无用信息,那么我们的例子不能最小。
产生一个错误的时候,我们通常会收到一个回溯显示哪些行代码产生错误。这是有帮助吗?是的!
例如在执行上面的代码在一个python脚本的终端,我们得到以下回溯:
回溯(最近的电话):
文件“/用户/教程/绝笔。py”, 67行,在<模块>
main ()
文件“/用户/教程/绝笔。py”,在主行53
模型= make_model(数据)
文件“/用户/教程/绝笔。在make_model py”线41
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
如果我们执行相同的代码在一个Jupyter笔记本我们回溯略有不同(但同样的错误),这是更丰富:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyError回溯(最近调用最后)
细胞在[441],67行
64打印(“\ n也许不可行模式。”)
66 if __name__ = =“__main__”:
推荐- - - - - - > 67 main ()
细胞在[441],53岁,在主要()
51 def main ():
52 data = make_data ()
- - - > = make_model 53模型(数据)
54 model.optimize ()
56如果model.getAttr(“状态”)= = GRB.OPTIMAL:
细胞在[441],41岁,在make_model(数据)
39我的范围(米):
40 h、l组合(范围(k), 2):
推荐- - - - - - > 41模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
42模型。addGenConstrPow (ox_2[我][h] [l], sox_2[我][h] [l], 0.5,“女朋友”,“FuncPieces = 1000”)
43个模型。addConstr (sox_3[我][h] [l] = = sox_2[我][h] [l] * var_cos [h] [l])
KeyError: 0
两个回溯告诉我们一些非常重要的。错误是在以下行:
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
现在我们知道这行有错误,我们应该问自己以下问题:
这条线在for循环执行,应该多次执行。它甚至执行一次吗?还是运行产生错误之前几次?
要回答这个让我们把一个print语句错误的线,上方也就是说,
打印(我、h、l)
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
当我们运行代码时,我们看到“0 0 1的输出打印错误消息。这是唯一的输出,所以我们现在知道,甚至不是执行一次。我们现在正处于一个好位置开始删除无用的代码。
删除后将执行的错误
如果代码将不会执行,因为之前发生错误,那么这段代码就不是有益的。我们应该删除一些线路(注释掉以下):
我的范围(米):
h、l组合(范围(k), 2):
打印(我、h、l)
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
#模型。addGenConstrPow (ox_2[我][h] [l], sox_2[我][h] [l], 0.5,“女朋友”,“FuncPieces = 1000”)
#模型。addConstr (sox_3[我][h] [l] = = sox_2[我][h] [l] * var_cos [h] [l])
#我的范围(米):
# ox_3 = 0
# h、l组合(范围(k), 2):
# ox_3 + = 2 * soc_3[我][h] [l]
#回归模型
自从发生错误make_model函数我们也可以删除任何代码后,发生在主要()函数:
def main ():
data = make_data ()
模型= make_model(数据)
# model.optimize ()
#如果model.getAttr(“状态”)= = GRB.OPTIMAL:
#在model.getVars var ():
#如果var.getAttr (x) ! = 0:
#打印(var.getAttr (“VarName”), var.getAttr (" X "))
#打印(“\ n达到最佳:{}“.format (model.getObjective () .getValue ()))
其他:
#打印(“\ n也许不可行模式。”)
if __name__ = =“__main__”:
main ()
删除for循环和修复我的价值观,j和l
因为我们知道错误的线会产生错误时(我、h、l)(0, 0, 1),我们不需要循环。他们不帮助,再一次,我们不应该删除任何有帮助的:
# x正常化
x_normed = x / x.sum(轴= 0)
我的范围(米):
模型。addConstr (ox_1[我]= =总和(var_w [h] * x_normed[我][h]的h范围(k)))
我、h、l = 0, 0, 1
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
删除无关的代码错误
让我们猜的模型参数设置附近的开始make_model功能不影响这一问题,并删除它们(我们将检查结束时,确保我们仍然得到同样的错误)。我们也可以试着删除一些进口不被使用。如果我们不小心删除的东西需要复制错误,然后我们将知道当我们测试重现性!
提示:一些代码编辑器和ide如Visual Studio代码使它很容易确定哪些变量和进口不习惯,通过颜色不同。
有几个变量、约束和导入语句与错误的行。删除它们后,我们有以下:
进口numpy np
从gurobipy导入模型,伽马线暴
出现从itertools进口组合
#数据
def make_data ():
ε= 1 e-6
#输入数据
k = 5
x = np。阵列([[1,1,1,1,- 1),
(1,1,1,1,- 1),
(1,1,1,1,1]])
m = 3
返回ε,x, k、m
def make_model(数据):
ε,x, k、m =数据
#空模型进行初始化
模型=模型(“模式”)
#添加变量
var_w = model.addVars(范围(k),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
ox_2 = model.addVars(范围(m),组合(范围(k), 2),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
# x正常化
x_normed = x / x.sum(轴= 0)
我、h、l = = 0, 0, 1
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
def main ():
data = make_data ()
模型= make_model(数据)
if __name__ = =“__main__”:
main ()
我们已经删除了很多代码。我们应该检查它仍然运行此代码给出了错误我们期待…它!
简化脚本通过删除功能
经常把代码放在函数将一个好主意。即使函数被称为一旦他们仍然可以帮助被自我记录如果命名——在本例中就是已经完成。但是我们不需要这些功能来创建一个绝笔——如果不是有用的然后是无益的!让我们删除它们:
进口numpy np
从gurobipy导入模型,伽马线暴
出现从itertools进口组合
ε= 1 e-6
#输入数据
k = 5
x = np。阵列([[1,1,1,1,- 1),
(1,1,1,1,- 1),
(1,1,1,1,1]])
m = 3
#空模型进行初始化
模型=模型(“模式”)
#添加变量
var_w = model.addVars(范围(k),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
ox_2 = model.addVars(范围(m),组合(范围(k), 2),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
# x正常化
x_normed = x / x.sum(轴= 0)
我、h、l = = 0, 0, 1
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
这是现在看起来好多了,我们缩小了重要部分。我们可以做得更好吗?我们可以试着找出部分线是生产错误!
到底是错误在哪里?
线产生的错误有很多事情发生。有调用addConstr、索引、乘法、约束定义……让我们分离出一些这些事情到自己的线,从简单到复杂,通过添加一些新行:
我、h、l = = 0, 0, 1
# - - - - - - - - - - -新行
ox_2[我][h] [l]
var_w [h]
x_normed[我][h]
var_w [l]
x_normed[我][l]
var_w [h] * x_normed[我][h]
var_w [h] * x_normed[我][h] * var_w [l]
var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l]
ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l]
# - - - - - - - - - - - - - - - - - - - - -
模型。addConstr (ox_2[我][h] [l] = = var_w [h] * x_normed[我][h] * var_w [l] * x_normed[我][l])
你能看到我们要做什么?当我们执行代码希望回溯会告诉我们错误的确切位置。回溯,结果是:
回溯(最近的电话):
文件“/用户/教程/绝笔。py”第23行,在<模块>
ox_2[我][h] [l]
KeyError: 0
我们遇到的问题在我们的第一线!有问题ox_2[我][h] [l]。
最简单的绝笔可能吗?
现在我们知道这个错误到底是什么我们可以减少我们的绝笔
从gurobipy导入模型,伽马线暴
出现从itertools进口组合
#输入数据
k = 5
m = 3
模型=模型(“模式”)
ox_2 = model.addVars(范围(m),组合(范围(k), 2),磅= 0,乌兰巴托= 1,vtype = GRB.CONTINUOUS)
我、h、l = = 0, 0, 1
ox_2[我][h] [l]
这是很好的。尽管技术上下面是最简单的绝笔这个例子:
从gurobipy导入模型
模型= ()
ox_2 =模型。addVars ([0], [0], [1])
ox_2 [0] [0] [1]
在这个例子中有一切需要复制错误,什么不是。
附录:发现问题的乐趣
虽然这篇文章的目的是展示如何绝笔可以生产,我们可以去一个小一步——找出错误——在这个例子中我们非常接近。
我们知道有问题与ox_2我们所做的。ox_2是什么?如果我们执行下面的代码
print(类型(ox_2))
然后Python ox_2是会告诉我们的gurobipy.tupledict
如果我们找到的文档manbet体育手机客户端gurobipy.tupledict然后我们看到,一半页面,如下:
例如,d [1, 2]
返回与元组相关联的值(1、2)
。
也许ox_2[0][0][1]是错误的语法呢?它应该被ox_2[0, 0, 1]吗?
我们测试这个绝笔,找到我们不再有一个错误。问题解决了!
评论
0评论
请登录留下你的评论。