【问题标题】:How can the performance of an Alloy model be improved?如何提高合金模型的性能?
【发布时间】:2018-06-27 11:25:49
【问题描述】:

我已经为“倒水难题”写了两个合金解决方案(给定一个 5 夸脱的水壶,一个 3 夸脱的水壶,你能准确测量 4 夸脱吗?)

我的第一次尝试(specific.als)将两个罐子硬编码为命名关系:

sig State {
  threeJug: one Int, 
  fiveJug: one Int 
}

它在大约 500 毫秒内解决了这个难题(找到一个反例)。

我的第二次尝试 (generic.als) 被编码为允许任意数量和不同尺寸的水壶:

sig State {
  jugAmounts: Jug -> one Int
}

它在大约 2000 毫秒内解决了这个难题。

通用代码是否可以像特定代码一样快速运行? 需要改变什么?

具体型号

open util/ordering[State]


sig State {
  threeJug: one Int, 
  fiveJug: one Int 
}


fact {
  all s: State {
    s.threeJug >= 0
    s.threeJug <= 3
    s.fiveJug >= 0
    s.fiveJug <= 5
  }

  first.threeJug = 0
  first.fiveJug = 0
}


pred Fill(s, s': State){
  (s'.threeJug = 3 and s'.fiveJug = s.fiveJug)
  or (s'.fiveJug = 5 and s'.threeJug = s.threeJug)
}


pred Empty(s, s': State){
  (s.threeJug > 0 and s'.threeJug = 0 and s'.fiveJug = s.fiveJug)
  or (s.fiveJug > 0 and s'.fiveJug = 0 and s'.threeJug = s.threeJug)
}


pred Pour3To5(s, s': State){
  some x: Int {
    s'.fiveJug = plus[s.fiveJug, x]
    and s'.threeJug = minus[s.threeJug, x]
    and (s'.fiveJug = 5 or s'.threeJug = 0)
  }
}


pred Pour5To3(s, s': State){
  some x: Int {
    s'.threeJug = plus[s.threeJug, x]
    and s'.fiveJug = minus[s.fiveJug, x]
    and (s'.threeJug = 3 or s'.fiveJug = 0)
  }
}


fact Next {
  all s: State, s': s.next {
    Fill[s, s']
    or Pour3To5[s, s']
    or Pour5To3[s, s']
    or Empty[s, s']
  }
}


assert notOne {
  no s: State | s.fiveJug = 4
}


check notOne for 7

通用模型

open util/ordering[State]


sig Jug {
  max: Int
}


sig State {
  jugAmounts: Jug -> one Int
}


fact jugCapacity {
  all s: State {
    all j: s.jugAmounts.univ {
      j.(s.jugAmounts) >= 0
      and j.(s.jugAmounts) <= j.max
    }
  }

  -- jugs start empty
  first.jugAmounts = Jug -> 0
}


pred fill(s, s': State){
  one j: Jug {
    j.(s'.jugAmounts) = j.max
    all r: Jug - j | r.(s'.jugAmounts) = r.(s.jugAmounts)
  }
}


pred empty(s, s': State){
  one j: Jug {
    j.(s'.jugAmounts) = 0
    all r: Jug - j | r.(s'.jugAmounts) = r.(s.jugAmounts)
  }
}


pred pour(s, s': State){
  some x: Int {
    some from, to: Jug {
      from.(s'.jugAmounts) = 0 or to.(s'.jugAmounts) = to.max
      from.(s'.jugAmounts) = minus[from.(s.jugAmounts), x]
      to.(s'.jugAmounts) = plus[to.(s.jugAmounts), x]
      all r: Jug - from - to | r.(s'.jugAmounts) = r.(s.jugAmounts)
    }
  }
}


fact next {
  all s: State, s': s.next {
    fill[s, s']
    or empty[s, s']
    or pour[s, s']
  }
}


fact SpecifyPuzzle{
  all s: State | #s.jugAmounts = 2
  one j: Jug | j.max = 5
  one j: Jug | j.max = 3
}


assert no4 {
  no s: State | 4 in univ.(s.jugAmounts)
}


check no4 for 7

【问题讨论】:

    标签: performance alloy


    【解决方案1】:

    根据经验,可以通过以下方式获得更好的性能:

    • 减小搜索空间的大小(通过减小范围、sig 和关系的数量、关系的数量……)
    • 简化约束。尽量避免使用集合理解和量化。例如,您的断言no s: State | 4 in univ.(s.jugAmounts) 在逻辑上等同于4 not in univ.(State.jugAmounts)。仅在您的模型中进行这个微小的更改已经使我对子句的处理速度加快了 200 毫秒。

    编辑

    我在空闲时间回到了你的问题。

    这是我用来得出这个结论的模型

    module WaterJugs/AbstractSyntax/ASM
    open util/ordering[State] 
    open util/integer
    
    
    //===== HARDCODE 2 jugs of volume 3 and 5 resp.  comment those lines for generic approach ====
    one sig JugA extends Jug{}{
        volume=3
        water[first]=0
    }
    one sig JugB extends Jug{}{
        volume=5
        water[first]=0
    }
    //==================================================================
    
    abstract sig Jug{
        volume: Int,
        water:  State  ->one Int
    }{
        all s:State| water[s]<=volume and water[s]>=0
        volume >0
    }
    
    pred Fill(s1,s2:State){
        one disj j1,j2:Jug{j1.water[s1]!=j1.volume and j1.water[s2]=j1.volume and j2.water[s2]=j2.water[s1] }
    }
    
    pred Empty(s1,s2:State){
        one disj j1,j2:Jug{ j1.water[s1]!=0 and  j1.water[s2]=0 and  j2.water[s2]= j2.water[s1] }
    }
    
    pred Pour(s1,s2:State){
        one disj j1,j2: Jug{
            add[j1.water[s1],j2.water[s1]] >j1.volume implies {
                ( j1.water[s2]=j1.volume and j2.water[s2]=sub[j2.water[s1],sub[j1.volume,j1.water[s1]]])}
            else{
                ( j1.water[s2]=add[j1.water[s1],j2.water[s1]] and j2.water[s2]=0)
            }
        }
    }
    
    fact Next {
      all s: State-last{
        Fill[s, s.next]
        or Pour[s, s.next]
        or Empty[s, s.next]
      }
    }
    
    sig State{
    
    }
    
    assert no4 {
      4 not in Jug.water[State]
    }
    
    check no4 for 7
    

    另外,这是Lightning提供的反例的可视化

    【讨论】:

    • 我无法通过减少量化来检测任何性能差异,例如将all s: State | #s.jugAmounts = 2 替换为#Jug = 2,或将no s: State | 4 in univ.(s.jugAmounts) 替换为4 not in univ.(State.jugAmounts)。可能存在差异,但由于 JVM 的即时编译器而难以衡量。
    • 有趣,我想量化性能提升的切实方法可能是比较生成的子句数量。这个数字越小越好。
    • 我有一些时间来摆弄你的例子,并用我的模型更新了答案。随意重复使用它
    • 感谢您的跟进 --- 我不熟悉您模型中的所有语法,也没有见过 Lightning,两者看起来都值得追求。对不起,如果我的模型的书面解释不清楚。绝对有可能将 4 夸脱装入罐中,这就是拼图的重点! = )
    • 此模型中的语法独立于 Lightning(它是纯合金,可与合金分析仪一起使用)。如果您需要一些帮助来理解一些句法结构,请不要犹豫:-)。如果您有兴趣尝试 Lightning,也是如此
    猜你喜欢
    • 1970-01-01
    • 2021-06-06
    • 1970-01-01
    • 2022-10-04
    • 1970-01-01
    • 1970-01-01
    • 2018-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多