fix incorrect folder name for julia-0.6.x

Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
2018-06-11 03:28:36 -07:00
parent 5e0e436e4e
commit 0e4acfb8f2
722 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license
module FieldValues
using ...VersionWeights
importall .....Base.Operators
export FieldValue, Field, validmax, secondmax
# FieldValue is a hierarchical numeric type with 6 levels.
# When summing two FieldValues, the levels are summed independently.
# When comparing them, lower levels take precedence.
# The levels are used as such:
# l0 : for hard constraints (dependencies and requirements)
# l1 : for favoring higher versions of the explicitly required
# packages
# l2 : for favoring higher versions of all other packages
# l3 : for favoring uninstallation of non-needed packages
# l4 : for favoring dependants over dependencies
# l5 : for symmetry-breaking random noise
#
struct FieldValue
l0::Int
l1::VersionWeight
l2::VersionWeight
l3::Int
l4::Int
l5::Int128
end
FieldValue(l0::Integer, l1::VersionWeight, l2::VersionWeight, l3::Integer, l4::Integer) = FieldValue(l0, l1, l2, l3, l4, Int128(0))
FieldValue(l0::Integer, l1::VersionWeight, l2::VersionWeight, l3::Integer) = FieldValue(l0, l1, l2, l3, 0)
FieldValue(l0::Integer, l1::VersionWeight, l2::VersionWeight) = FieldValue(l0, l1, l2, 0)
FieldValue(l0::Integer, l1::VersionWeight) = FieldValue(l0, l1, zero(VersionWeight))
FieldValue(l0::Integer) = FieldValue(l0, zero(VersionWeight))
FieldValue() = FieldValue(0)
# This isn't nice, but it's for debugging only anyway
function Base.show(io::IO, a::FieldValue)
print(io, a.l0)
a == FieldValue(a.l0) && return
print(io, ".", a.l1)
a == FieldValue(a.l0, a.l1) && return
print(io, ".", a.l2)
a == FieldValue(a.l0, a.l1, a.l2) && return
print(io, ".", a.l3)
a == FieldValue(a.l0, a.l1, a.l2, a.l3) && return
print(io, ".", a.l4)
a == FieldValue(a.l0, a.l1, a.l2, a.l3, a.l4) && return
print(io, ".", a.l5)
return
end
const Field = Vector{FieldValue}
Base.zero(::Type{FieldValue}) = FieldValue()
Base.typemin(::Type{FieldValue}) = (x=typemin(Int); y=typemin(VersionWeight); FieldValue(x, y, y, x, x, typemin(Int128)))
Base.:-(a::FieldValue, b::FieldValue) = FieldValue(a.l0-b.l0, a.l1-b.l1, a.l2-b.l2, a.l3-b.l3, a.l4-b.l4, a.l5-b.l5)
Base.:+(a::FieldValue, b::FieldValue) = FieldValue(a.l0+b.l0, a.l1+b.l1, a.l2+b.l2, a.l3+b.l3, a.l4+b.l4, a.l5+b.l5)
function Base.isless(a::FieldValue, b::FieldValue)
a.l0 < b.l0 && return true
a.l0 > b.l0 && return false
c = cmp(a.l1, b.l1)
c < 0 && return true
c > 0 && return false
c = cmp(a.l2, b.l2)
c < 0 && return true
c > 0 && return false
a.l3 < b.l3 && return true
a.l3 > b.l3 && return false
a.l4 < b.l4 && return true
a.l4 > b.l4 && return false
a.l5 < b.l5 && return true
return false
end
Base.:(==)(a::FieldValue, b::FieldValue) =
a.l0 == b.l0 && a.l1 == b.l1 && a.l2 == b.l2 && a.l3 == b.l3 && a.l4 == b.l4 && a.l5 == b.l5
Base.abs(a::FieldValue) = FieldValue(abs(a.l0), abs(a.l1), abs(a.l2), abs(a.l3), abs(a.l4), abs(a.l5))
Base.copy(a::FieldValue) = FieldValue(a.l0, copy(a.l1), copy(a.l2), a.l3, a.l4, a.l5)
function Base.unsafe_copy!(dest::Field, doffs, src::Field, soffs, n)
for i = 1:n
dest[doffs+i-1] = copy(src[soffs+i-1])
end
return dest
end
# if the maximum field has l0 < 0, it means that
# some hard constraint is being violated
validmax(a::FieldValue) = a.l0 >= 0
# like usual indmax, but favors the highest indices
# in case of a tie
function Base.indmax(f::Field)
m = typemin(FieldValue)
mi = 0
for j = length(f):-1:1
if f[j] > m
m = f[j]
mi = j
end
end
@assert mi != 0
return mi
end
# secondmax returns the (normalized) value of the second maximum in a
# field. It's used to determine the most polarized field.
function secondmax(f::Field)
m = typemin(FieldValue)
m2 = typemin(FieldValue)
for i = 1:length(f)
a = f[i]
if a > m
m2 = m
m = a
elseif a > m2
m2 = a
end
end
return m2 - m
end
end

View File

@@ -0,0 +1,364 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license
module PkgToMaxSumInterface
using ...Types, ...Query, ..VersionWeights
export Interface, compute_output_dict, greedysolver,
verify_solution, enforce_optimality!
# A collection of objects which allow interfacing external (Pkg) and
# internal (MaxSum) representation
mutable struct Interface
# requirements and dependencies, in external representation
reqs::Requires
deps::Dict{String,Dict{VersionNumber,Available}}
# packages list
pkgs::Vector{String}
# number of packages
np::Int
# states per package: one per version + uninstalled
spp::Vector{Int}
# pakage dict: associates an index to each package name
pdict::Dict{String,Int}
# package versions: for each package, keep the list of the
# possible version numbers; this defines a
# mapping from version numbers of a package
# to indices
pvers::Vector{Vector{VersionNumber}}
# versions dict: associates a version index to each package
# version; such that
# pvers[p0][vdict[p0][vn]] = vn
vdict::Vector{Dict{VersionNumber,Int}}
# version weights: the weight for each version of each package
# (versions include the uninstalled state; the
# higher the weight, the more favored the version)
vweight::Vector{Vector{VersionWeight}}
function Interface(reqs::Requires, deps::Dict{String,Dict{VersionNumber,Available}})
# generate pkgs
pkgs = sort!(String[keys(deps)...])
np = length(pkgs)
# generate pdict
pdict = Dict{String,Int}(pkgs[i] => i for i = 1:np)
# generate spp and pvers
spp = Vector{Int}(np)
pvers = [VersionNumber[] for i = 1:np]
for (p,depsp) in deps, vn in keys(depsp)
p0 = pdict[p]
push!(pvers[p0], vn)
end
for p0 = 1:np
sort!(pvers[p0])
spp[p0] = length(pvers[p0]) + 1
end
# generate vdict
vdict = [Dict{VersionNumber,Int}() for p0 = 1:np]
for (p,depsp) in deps
p0 = pdict[p]
vdict0 = vdict[p0]
pvers0 = pvers[p0]
for vn in keys(depsp)
for v0 in 1:length(pvers0)
if pvers0[v0] == vn
vdict0[vn] = v0
break
end
end
end
end
## generate wveights:
vweight = Vector{Vector{VersionWeight}}(np)
for p0 = 1:np
pvers0 = pvers[p0]
spp0 = spp[p0]
vweight0 = vweight[p0] = Vector{VersionWeight}(spp0)
for v0 = 1:spp0-1
vweight0[v0] = VersionWeight(pvers0[v0])
end
vweight0[spp0] = VersionWeight(v"0") # last version means uninstalled
end
return new(reqs, deps, pkgs, np, spp, pdict, pvers, vdict, vweight)
end
end
# The output format is a Dict which associates a VersionNumber to each installed package name
function compute_output_dict(sol::Vector{Int}, interface::Interface)
pkgs = interface.pkgs
np = interface.np
pvers = interface.pvers
spp = interface.spp
want = Dict{String,VersionNumber}()
for p0 = 1:np
p = pkgs[p0]
s = sol[p0]
if s != spp[p0]
v = pvers[p0][s]
want[p] = v
end
end
return want
end
# Produce a trivial solution: try to maximize each version;
# bail out as soon as some non-trivial requirements are detected.
function greedysolver(interface::Interface)
reqs = interface.reqs
deps = interface.deps
spp = interface.spp
pdict = interface.pdict
pvers = interface.pvers
np = interface.np
# initialize solution: all uninstalled
sol = [spp[p0] for p0 = 1:np]
# set up required packages to their highest allowed versions
for (rp,rvs) in reqs
rp0 = pdict[rp]
# look for the highest version which satisfies the requirements
rv = spp[rp0] - 1
while rv > 0
rvn = pvers[rp0][rv]
rvn rvs && break
rv -= 1
end
@assert rv > 0
sol[rp0] = rv
end
# we start from required packages and explore the graph
# following dependencies
staged = Set{String}(keys(reqs))
seen = copy(staged)
while !isempty(staged)
staged_next = Set{String}()
for p in staged
p0 = pdict[p]
@assert sol[p0] < spp[p0]
vn = pvers[p0][sol[p0]]
a = deps[p][vn]
# scan dependencies
for (rp,rvs) in a.requires
rp0 = pdict[rp]
# look for the highest version which satisfies the requirements
rv = spp[rp0] - 1
while rv > 0
rvn = pvers[rp0][rv]
rvn rvs && break
rv -= 1
end
# if we found a version, and the package was uninstalled
# or the same version was already selected, we're ok;
# otherwise we can't be sure what the optimal configuration is
# and we bail out
if rv > 0 && (sol[rp0] == spp[rp0] || sol[rp0] == rv)
sol[rp0] = rv
else
return (false, Int[])
end
rp seen || push!(staged_next, rp)
end
end
union!(seen, staged_next)
staged = staged_next
end
@assert verify_solution(sol, interface)
return true, sol
end
# verifies that the solution fulfills all hard constraints
# (requirements and dependencies)
function verify_solution(sol::Vector{Int}, interface::Interface)
reqs = interface.reqs
deps = interface.deps
spp = interface.spp
pdict = interface.pdict
pvers = interface.pvers
vdict = interface.vdict
# verify requirements
for (p,vs) in reqs
p0 = pdict[p]
sol[p0] != spp[p0] || return false
vn = pvers[p0][sol[p0]]
vn vs || return false
end
# verify dependencies
for (p,d) in deps
p0 = pdict[p]
vdict0 = vdict[p0]
for (vn,a) in d
v0 = vdict0[vn]
if sol[p0] == v0
for (rp, rvs) in a.requires
p1 = pdict[rp]
if sol[p1] == spp[p1]
println("""
VERIFICATION ERROR: REQUIRED DEPENDENCY NOT INSTALLED
package p=$p (p0=$p0) version=$vn (v0=$v0) requires package rp=$rp in version set rvs=$rvs
but package $rp is not being installed (p1=$p1 sol[p1]=$(sol[p1]) == spp[p1]=$(spp[p1]))
""")
return false
end
vn1 = pvers[p1][sol[p1]]
if vn1 rvs
println("""
VERIFICATION ERROR: INVALID VERSION
package p=$p (p0=$p0) version=$vn (v0=$v0) requires package rp=$rp in version set rvs=$rvs
but package $rp version is being set to $vn1 (p1=$p1 sol[p1]=$(sol[p1]) spp[p1]=$(spp[p1]))
""")
return false
end
end
end
end
end
return true
end
# Push the given solution to a local optimium if needed
function enforce_optimality!(sol::Vector{Int}, interface::Interface)
np = interface.np
reqs = interface.reqs
deps = interface.deps
pkgs = interface.pkgs
spp = interface.spp
pdict = interface.pdict
pvers = interface.pvers
vdict = interface.vdict
# prepare some useful structures
# pdeps[p0][v0] has all dependencies of package p0 version v0
pdeps = [Vector{Requires}(spp[p0]-1) for p0 = 1:np]
# prevdeps[p1][p0][v0] is the VersionSet of package p1 which package p0 version v0
# depends upon
prevdeps = [Dict{Int,Dict{Int,VersionSet}}() for p0 = 1:np]
for (p,d) in deps
p0 = pdict[p]
vdict0 = vdict[p0]
for (vn,a) in d
v0 = vdict0[vn]
pdeps[p0][v0] = a.requires
for (rp, rvs) in a.requires
p1 = pdict[rp]
if !haskey(prevdeps[p1], p0)
prevdeps[p1][p0] = Dict{Int,VersionSet}()
end
prevdeps[p1][p0][v0] = rvs
end
end
end
restart = true
while restart
restart = false
for p0 = 1:np
s0 = sol[p0]
if s0 >= spp[p0] - 1
# either the package is not installed,
# or it's already at the maximum version
continue
end
viol = false
# check if the higher version has a depencency which
# would be violated by the state of the remaining packages
for (p,vs) in pdeps[p0][s0+1]
p1 = pdict[p]
if sol[p1] == spp[p1]
# the dependency is violated because
# the other package is not being installed
viol = true
break
end
vn = pvers[p1][sol[p1]]
if vn vs
# the dependency is violated because
# the other package version is invalid
viol = true
break
end
end
viol && continue
# check if bumping the version would violate some
# dependency of another package
for (p1,d) in prevdeps[p0]
vs = get(d, sol[p1], nothing)
vs === nothing && continue
vn = pvers[p0][s0+1]
if vn vs
# bumping the version would violate
# the dependency
viol = true
break
end
end
viol && continue
# So the solution is non-optimal: we bump it manually
#warn("nonoptimal solution for package $(interface.pkgs[p0]): sol=$s0")
sol[p0] += 1
restart = true
end
end
# Finally uninstall unneeded packages:
# start from the required ones and keep only
# the packages reachable from them along the graph
uninst = trues(np)
staged = Set{String}(keys(reqs))
seen = copy(staged)
while !isempty(staged)
staged_next = Set{String}()
for p in staged
p0 = pdict[p]
uninst[p0] = false
@assert sol[p0] < spp[p0]
vn = pvers[p0][sol[p0]]
a = deps[p][vn]
# scan dependencies
for (rp,rvs) in a.requires
rp0 = pdict[rp]
@assert sol[rp0] < spp[rp0] && pvers[rp0][sol[rp0]] rvs
rp seen || push!(staged_next, rp)
end
end
union!(seen, staged_next)
staged = staged_next
end
for p0 in find(uninst)
sol[p0] = spp[p0]
end
return
end
end

View File

@@ -0,0 +1,524 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license
module MaxSum
include("fieldvalue.jl")
using .FieldValues, ..VersionWeights, ..PkgToMaxSumInterface
export UnsatError, Graph, Messages, maxsum
# An exception type used internally to signal that an unsatisfiable
# constraint was detected
mutable struct UnsatError <: Exception
info
end
# Some parameters to drive the decimation process
mutable struct MaxSumParams
nondec_iterations # number of initial iterations before starting
# decimation
dec_interval # number of iterations between decimations
dec_fraction # fraction of nodes to decimate at every decimation
# step
function MaxSumParams()
accuracy = parse(Int, get(ENV, "JULIA_PKGRESOLVE_ACCURACY", "1"))
if accuracy <= 0
error("JULIA_PKGRESOLVE_ACCURACY must be > 0")
end
nondec_iterations = accuracy * 20
dec_interval = accuracy * 10
dec_fraction = 0.05 / accuracy
return new(nondec_iterations, dec_interval, dec_fraction)
end
end
# Graph holds the graph structure onto which max-sum is run, in
# sparse format
mutable struct Graph
# adjacency matrix:
# for each package, has the list of neighbors
# indices (both dependencies and dependants)
gadj::Vector{Vector{Int}}
# compatibility mask:
# for each package p0 has a list of bool masks.
# Each entry in the list gmsk[p0] is relative to the
# package p1 as read from gadj[p0].
# Each mask has dimension spp1 x spp0, where
# spp0 is the number of states of p0, and
# spp1 is the number of states of p1.
gmsk::Vector{Vector{BitMatrix}}
# dependency direction:
# keeps track of which direction the dependency goes
# takes 3 values:
# 1 = dependant
# -1 = dependency
# 0 = both
# Used to break symmetry between dependants and
# dependencies (introduces a FieldValue at level l3).
# The "both" case is for when there are dependency
# relations which go both ways, in which case the
# noise is left to discriminate in case of ties
gdir::Vector{Vector{Int}}
# adjacency dict:
# allows one to retrieve the indices in gadj, so that
# gadj[p0][adjdict[p1][p0]] = p1
# ("At which index does package p1 appear in gadj[p0]?")
adjdict::Vector{Dict{Int,Int}}
# states per package: same as in Interface
spp::Vector{Int}
# update order: shuffled at each iteration
perm::Vector{Int}
# number of packages (all Vectors above have this length)
np::Int
function Graph(interface::Interface)
deps = interface.deps
np = interface.np
spp = interface.spp
pdict = interface.pdict
pvers = interface.pvers
vdict = interface.vdict
gadj = [Int[] for i = 1:np]
gmsk = [BitMatrix[] for i = 1:np]
gdir = [Int[] for i = 1:np]
adjdict = [Dict{Int,Int}() for i = 1:np]
for (p,d) in deps
p0 = pdict[p]
vdict0 = vdict[p0]
for (vn,a) in d
v0 = vdict0[vn]
for (rp, rvs) in a.requires
p1 = pdict[rp]
j0 = 1
while j0 <= length(gadj[p0]) && gadj[p0][j0] != p1
j0 += 1
end
j1 = 1
while j1 <= length(gadj[p1]) && gadj[p1][j1] != p0
j1 += 1
end
@assert (j0 > length(gadj[p0]) && j1 > length(gadj[p1])) ||
(j0 <= length(gadj[p0]) && j1 <= length(gadj[p1]))
if j0 > length(gadj[p0])
push!(gadj[p0], p1)
push!(gadj[p1], p0)
j0 = length(gadj[p0])
j1 = length(gadj[p1])
adjdict[p1][p0] = j0
adjdict[p0][p1] = j1
bm = trues(spp[p1], spp[p0])
bmt = bm'
push!(gmsk[p0], bm)
push!(gmsk[p1], bmt)
push!(gdir[p0], 1)
push!(gdir[p1], -1)
else
bm = gmsk[p0][j0]
bmt = gmsk[p1][j1]
if gdir[p0][j0] == -1
gdir[p0][j0] = 0
gdir[p1][j1] = 0
end
end
for v1 = 1:length(pvers[p1])
if pvers[p1][v1] rvs
bm[v1, v0] = false
bmt[v0, v1] = false
end
end
bm[end,v0] = false
bmt[v0,end] = false
end
end
end
perm = [1:np;]
return new(gadj, gmsk, gdir, adjdict, spp, perm, np)
end
end
# Messages has the cavity messages and the total fields, and
# gets updated iteratively (and occasionally decimated) until
# convergence
mutable struct Messages
# cavity incoming messages: for each package p0,
# for each neighbor p1 of p0,
# msg[p0][p1] is a vector of length spp[p0]
# messages are normalized (i.e. the max is always 0)
msg::Vector{Vector{Field}}
# overall fields: for each package p0,
# fld[p0] is a vector of length spp[p0]
# fields are not normalized
fld::Vector{Field}
# backup of the initial value of fld, to be used when resetting
initial_fld::Vector{Field}
# keep track of which variables have been decimated
decimated::BitVector
num_nondecimated::Int
function Messages(interface::Interface, graph::Graph)
reqs = interface.reqs
pkgs = interface.pkgs
np = interface.np
spp = interface.spp
pvers = interface.pvers
pdict = interface.pdict
vweight = interface.vweight
# a "deterministic noise" function based on hashes
function noise(p0::Int, v0::Int)
s = pkgs[p0] * string(v0 == spp[p0] ? "UNINST" : pvers[p0][v0])
Int128(hash(s))
end
# external fields: there are 2 terms, a noise to break potential symmetries
# and one to favor newest versions over older, and no-version over all
fld = [[FieldValue(0, zero(VersionWeight), vweight[p0][v0], (v0==spp[p0]), 0, noise(p0,v0)) for v0 = 1:spp[p0]] for p0 = 1:np]
# enforce requirements
for (rp, rvs) in reqs
p0 = pdict[rp]
pvers0 = pvers[p0]
fld0 = fld[p0]
for v0 = 1:spp[p0]-1
vn = pvers0[v0]
if !in(vn, rvs)
# the state is forbidden by requirements
fld0[v0] = FieldValue(-1)
else
# the state is one of those explicitly requested:
# favor it at a higer level than normal (upgrade
# FieldValue from l2 to l1)
fld0[v0] += FieldValue(0, vweight[p0][v0], -vweight[p0][v0])
end
end
# the uninstalled state is forbidden by requirements
fld0[spp[p0]] = FieldValue(-1)
end
# normalize fields
for p0 = 1:np
m = maximum(fld[p0])
for v0 = 1:spp[p0]
fld[p0][v0] -= m
end
end
initial_fld = deepcopy(fld)
# initialize cavity messages to 0
gadj = graph.gadj
msg = [[zeros(FieldValue, spp[p0]) for p1 = 1:length(gadj[p0])] for p0 = 1:np]
return new(msg, fld, initial_fld, falses(np), np)
end
end
function getsolution(msgs::Messages)
# the solution is just the location of the maximum in
# each field
fld = msgs.fld
np = length(fld)
sol = Vector{Int}(np)
for p0 = 1:np
fld0 = fld[p0]
s0 = indmax(fld0)
if !validmax(fld0[s0])
throw(UnsatError(p0))
end
sol[p0] = s0
end
return sol
end
# This is the core of the max-sum solver:
# for a given node p0 (i.e. a package) updates all
# input cavity messages and fields of its neighbors
function update(p0::Int, graph::Graph, msgs::Messages)
gadj = graph.gadj
gmsk = graph.gmsk
gdir = graph.gdir
adjdict = graph.adjdict
spp = graph.spp
np = graph.np
msg = msgs.msg
fld = msgs.fld
decimated = msgs.decimated
maxdiff = zero(FieldValue)
gadj0 = gadj[p0]
msg0 = msg[p0]
fld0 = fld[p0]
spp0 = spp[p0]
adjdict0 = adjdict[p0]
# iterate over all neighbors of p0
for j0 in 1:length(gadj0)
p1 = gadj0[j0]
decimated[p1] && continue
j1 = adjdict0[p1]
#@assert j0 == adjdict[p1][p0]
bm1 = gmsk[p1][j1]
dir1 = gdir[p1][j1]
spp1 = spp[p1]
msg1 = msg[p1]
# compute the output cavity message p0->p1
cavmsg = fld0 - msg0[j0]
if dir1 == -1
# p0 depends on p1
for v0 = 1:spp0-1
cavmsg[v0] += FieldValue(0, VersionWeight(0), VersionWeight(0), 0, v0)
end
end
# keep the old input cavity message p0->p1
oldmsg = msg1[j1]
# init the new message to minus infinity
newmsg = [FieldValue(-1) for v1 = 1:spp1]
# compute the new message by passing cavmsg
# through the constraint encoded in the bitmask
# (nearly equivalent to:
# newmsg = [maximum(cavmsg[bm1[:,v1]]) for v1 = 1:spp1]
# except for the gnrg term)
m = FieldValue(-1)
for v1 = 1:spp1
for v0 = 1:spp0
if bm1[v0, v1]
newmsg[v1] = max(newmsg[v1], cavmsg[v0])
end
end
if dir1 == 1 && v1 != spp1
# p1 depends on p0
newmsg[v1] += FieldValue(0, VersionWeight(0), VersionWeight(0), 0, v1)
end
m = max(m, newmsg[v1])
end
if !validmax(m)
# No state available without violating some
# hard constraint
throw(UnsatError(p1))
end
# normalize the new message
for v1 = 1:spp1
newmsg[v1] -= m
end
diff = newmsg - oldmsg
maxdiff = max(maxdiff, maximum(abs.(diff)))
# update the field of p1
fld1 = fld[p1]
for v1 = 1:spp1
fld1[v1] += diff[v1]
end
# put the newly computed message in place
msg1[j1] = newmsg
end
return maxdiff
end
# A simple shuffling machinery for the update order in iterate()
# (woulnd't pass any random quality test but it's arguably enough)
let step=1
global shuffleperm, shuffleperminit
shuffleperminit() = (step = 1)
function shuffleperm(graph::Graph)
perm = graph.perm
np = graph.np
for j = np:-1:2
k = mod(step,j)+1
perm[j], perm[k] = perm[k], perm[j]
step += isodd(j) ? 1 : k
end
#@assert isperm(perm)
end
end
# Call update for all nodes (i.e. packages) in
# random order
function iterate(graph::Graph, msgs::Messages)
np = graph.np
maxdiff = zero(FieldValue)
shuffleperm(graph)
perm = graph.perm
for p0 in perm
maxdiff0 = update(p0, graph, msgs)
maxdiff = max(maxdiff, maxdiff0)
end
return maxdiff
end
function decimate1(p0::Int, graph::Graph, msgs::Messages)
decimated = msgs.decimated
fld = msgs.fld
adjdict = graph.adjdict
gmsk = graph.gmsk
@assert !decimated[p0]
fld0 = fld[p0]
s0 = indmax(fld0)
# only do the decimation if it is consistent with
# the previously decimated nodes
for p1 in find(decimated)
haskey(adjdict[p0], p1) || continue
s1 = indmax(fld[p1])
j1 = adjdict[p0][p1]
gmsk[p1][j1][s0,s1] || return false
end
#println("DECIMATING $p0 (s0=$s0 fld=$fld0)")
for v0 = 1:length(fld0)
v0 == s0 && continue
fld0[v0] = FieldValue(-1)
end
msgs.decimated[p0] = true
msgs.num_nondecimated -= 1
return true
end
function reset_messages!(msgs::Messages)
msg = msgs.msg
fld = msgs.fld
initial_fld = msgs.initial_fld
decimated = msgs.decimated
np = length(fld)
for p0 = 1:np
map(m->fill!(m, zero(FieldValue)), msg[p0])
decimated[p0] && continue
fld[p0] = copy(initial_fld[p0])
end
return msgs
end
# If normal convergence fails (or is too slow) fix the most
# polarized packages by adding extra infinite fields on every state
# but the maximum
function decimate(n::Int, graph::Graph, msgs::Messages)
#println("DECIMATING $n NODES")
adjdict = graph.adjdict
fld = msgs.fld
decimated = msgs.decimated
fldorder = sortperm(fld, by=secondmax)
did_dec = false
for p0 in fldorder
decimated[p0] && continue
did_dec |= decimate1(p0, graph, msgs)
n -= 1
n == 0 && break
end
@assert n == 0
if !did_dec
# did not succeed in decimating anything;
# try to decimate at least one node
for p0 in fldorder
decimated[p0] && continue
if decimate1(p0, graph, msgs)
did_dec = true
break
end
end
end
if !did_dec
# still didn't succeed, give up
p0 = first(fldorder[.~(decimated)])
throw(UnsatError(p0))
end
reset_messages!(msgs)
return
end
# In case ties still exist at convergence, break them and
# keep converging
function break_ties(msgs::Messages)
fld = msgs.fld
unbroken_ties = Int[]
for p0 = 1:length(fld)
fld0 = fld[p0]
z = 0
m = typemin(FieldValue)
for v0 = 1:length(fld0)
if fld0[v0] > m
m = fld0[v0]
z = 1
elseif fld0[v0] == m
z += 1
end
end
if z > 1
#println("TIE! p0=$p0")
decimate1(p0, msgs) && return false
push!(unbroken_ties, p0)
end
end
# If there were ties, but none were broken, bail out
isempty(unbroken_ties) || throw(PkgError(first(unbroken_ties)))
return true
end
# Iterative solver: run iterate() until convergence
# (occasionally calling decimate())
function maxsum(graph::Graph, msgs::Messages)
params = MaxSumParams()
it = 0
shuffleperminit()
while true
it += 1
maxdiff = iterate(graph, msgs)
#println("it = $it maxdiff = $maxdiff")
if maxdiff == zero(FieldValue)
break_ties(msgs) && break
continue
end
if it >= params.nondec_iterations &&
(it - params.nondec_iterations) % params.dec_interval == 0
numdec = clamp(floor(Int, params.dec_fraction * graph.np), 1, msgs.num_nondecimated)
decimate(numdec, graph, msgs)
msgs.num_nondecimated == 0 && break
end
end
# Finally, decimate everything just to
# check against inconsistencies
# (old_numnondec is saved just to prevent
# wrong messages about accuracy)
old_numnondec = msgs.num_nondecimated
decimate(msgs.num_nondecimated, graph, msgs)
msgs.num_nondecimated = old_numnondec
return getsolution(msgs)
end
end

View File

@@ -0,0 +1,230 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license
module VersionWeights
importall ....Base.Operators
export VersionWeight
struct HierarchicalValue{T}
v::Vector{T}
rest::T
end
HierarchicalValue{T}(v::Vector{T}) = HierarchicalValue{T}(v, zero(T))
HierarchicalValue(T::Type) = HierarchicalValue(T[])
Base.zero(::Type{HierarchicalValue{T}}) where {T} = HierarchicalValue(T)
Base.typemin(::Type{HierarchicalValue{T}}) where {T} = HierarchicalValue(T[], typemin(T))
for f in (:-, :+)
@eval function Base.$f(a::HierarchicalValue{T}, b::HierarchicalValue{T}) where T
av = a.v
bv = b.v
la = length(a.v)
lb = length(b.v)
l0 = min(la, lb)
l1 = max(la, lb)
ld = la - lb
rv = Vector{T}(l1)
rf = ($f)(a.rest, b.rest)
@inbounds for i = 1:l0
rv[i] = ($f)(av[i], bv[i])
end
@inbounds for i = l0+1:l0+ld
rv[i] = ($f)(av[i], b.rest)
end
@inbounds for i = l0+1:l0-ld
rv[i] = ($f)(a.rest, bv[i])
end
return HierarchicalValue(rv, rf)
end
end
Base.:-(a::HierarchicalValue) = HierarchicalValue(-a.v, -a.rest)
function Base.cmp(a::HierarchicalValue{T}, b::HierarchicalValue{T}) where T
av = a.v
bv = b.v
la = length(a.v)
lb = length(b.v)
l0 = min(la, lb)
l1 = max(la, lb)
ld = la - lb
@inbounds for i = 1:l0
c = cmp(av[i], bv[i]); c != 0 && return c
end
@inbounds for i = l0+1:l0+ld
c = cmp(av[i], b.rest); c != 0 && return c
end
@inbounds for i = l0+1:l0-ld
c = cmp(a.rest, bv[i]); c != 0 && return c
end
return cmp(a.rest, b.rest)
end
Base.isless(a::HierarchicalValue{T}, b::HierarchicalValue{T}) where {T} = cmp(a,b) < 0
Base.:(==)(a::HierarchicalValue{T}, b::HierarchicalValue{T}) where {T} = cmp(a,b) == 0
Base.abs(a::HierarchicalValue{T}) where {T} = HierarchicalValue(T[abs(x) for x in a.v], abs(a.rest))
Base.copy(a::HierarchicalValue{T}) where {T} = HierarchicalValue(T[copy(x) for x in a.v], copy(a.rest))
struct VWPreBuildItem
nonempty::Int
s::HierarchicalValue{Int}
i::Int
end
VWPreBuildItem() = VWPreBuildItem(0, HierarchicalValue(Int), 0)
VWPreBuildItem(i::Int) = VWPreBuildItem(1, HierarchicalValue(Int), i)
VWPreBuildItem(s::String) = VWPreBuildItem(1, HierarchicalValue(Int[s...]), 0)
Base.zero(::Type{VWPreBuildItem}) = VWPreBuildItem()
Base.typemin(::Type{VWPreBuildItem}) = (x=typemin(Int); VWPreBuildItem(x, typemin(HierarchicalValue{Int}), x))
Base.:-(a::VWPreBuildItem, b::VWPreBuildItem) = VWPreBuildItem(a.nonempty-b.nonempty, a.s-b.s, a.i-b.i)
Base.:+(a::VWPreBuildItem, b::VWPreBuildItem) = VWPreBuildItem(a.nonempty+b.nonempty, a.s+b.s, a.i+b.i)
Base.:-(a::VWPreBuildItem) = VWPreBuildItem(-a.nonempty, -a.s, -a.i)
function Base.cmp(a::VWPreBuildItem, b::VWPreBuildItem)
c = cmp(a.nonempty, b.nonempty); c != 0 && return c
c = cmp(a.s, b.s); c != 0 && return c
return cmp(a.i, b.i)
end
Base.isless(a::VWPreBuildItem, b::VWPreBuildItem) = cmp(a,b) < 0
Base.:(==)(a::VWPreBuildItem, b::VWPreBuildItem) = cmp(a,b) == 0
Base.abs(a::VWPreBuildItem) = VWPreBuildItem(abs(a.nonempty), abs(a.s), abs(a.i))
Base.copy(a::VWPreBuildItem) = VWPreBuildItem(a.nonempty, copy(a.s), a.i)
struct VWPreBuild
nonempty::Int
w::HierarchicalValue{VWPreBuildItem}
end
const _vwprebuild_zero = VWPreBuild(0, HierarchicalValue(VWPreBuildItem))
function VWPreBuild(ispre::Bool, desc::Tuple{Vararg{Union{Int,String}}})
isempty(desc) && return _vwprebuild_zero
desc == ("",) && return VWPreBuild(ispre ? -1 : 1, HierarchicalValue(VWPreBuildItem[]))
hv = HierarchicalValue([VWPreBuildItem(item) for item in desc])
return VWPreBuild(ispre ? -1 : 0, hv)
end
VWPreBuild() = _vwprebuild_zero
Base.zero(::Type{VWPreBuild}) = VWPreBuild()
const _vwprebuild_min = VWPreBuild(typemin(Int), typemin(HierarchicalValue{VWPreBuildItem}))
Base.typemin(::Type{VWPreBuild}) = _vwprebuild_min
function Base.:(-)(a::VWPreBuild, b::VWPreBuild)
b === _vwprebuild_zero && return a
a === _vwprebuild_zero && return -b
VWPreBuild(a.nonempty-b.nonempty, a.w-b.w)
end
function Base.:(+)(a::VWPreBuild, b::VWPreBuild)
b === _vwprebuild_zero && return a
a === _vwprebuild_zero && return b
VWPreBuild(a.nonempty+b.nonempty, a.w+b.w)
end
function Base.:(-)(a::VWPreBuild)
a === _vwprebuild_zero && return a
VWPreBuild(-a.nonempty, -a.w)
end
@inline function Base.cmp(a::VWPreBuild, b::VWPreBuild)
a === _vwprebuild_zero && b === _vwprebuild_zero && return 0
c = cmp(a.nonempty, b.nonempty); c != 0 && return c
return cmp(a.w, b.w)
end
Base.isless(a::VWPreBuild, b::VWPreBuild) = cmp(a,b) < 0
Base.:(==)(a::VWPreBuild, b::VWPreBuild) = cmp(a,b) == 0
function Base.abs(a::VWPreBuild)
a === _vwprebuild_zero && return a
VWPreBuild(abs(a.nonempty), abs(a.w))
end
function Base.copy(a::VWPreBuild)
a === _vwprebuild_zero && return a
VWPreBuild(a.nonempty, copy(a.w))
end
function Base.deepcopy_internal(a::VWPreBuild, dict::ObjectIdDict)
haskey(dict, a) && return dict[a]
b = (a === _vwprebuild_zero) ? _vwprebuild_zero : VWPreBuild(a.nonempty, Base.deepcopy_internal(a.w, dict))
dict[a] = b
return b
end
# The numeric type used to determine how the different
# versions of a package should be weighed
struct VersionWeight
major::Int
minor::Int
patch::Int
prerelease::VWPreBuild
build::VWPreBuild
end
VersionWeight(major::Int, minor::Int, patch::Int, prerelease::VWPreBuild) = VersionWeight(major, minor, patch, prerelease, zero(VWPreBuild))
VersionWeight(major::Int, minor::Int, patch::Int) = VersionWeight(major, minor, patch, zero(VWPreBuild))
VersionWeight(major::Int, minor::Int) = VersionWeight(major, minor, 0)
VersionWeight(major::Int) = VersionWeight(major, 0)
VersionWeight() = VersionWeight(0)
VersionWeight(vn::VersionNumber) =
VersionWeight(vn.major, vn.minor, vn.patch,
VWPreBuild(true, vn.prerelease), VWPreBuild(false, vn.build))
Base.zero(::Type{VersionWeight}) = VersionWeight()
Base.typemin(::Type{VersionWeight}) = (x=typemin(Int); y=typemin(VWPreBuild); VersionWeight(x, x, x, y, y))
Base.:(-)(a::VersionWeight, b::VersionWeight) =
VersionWeight(a.major-b.major, a.minor-b.minor, a.patch-b.patch,
a.prerelease-b.prerelease, a.build-b.build)
Base.:(+)(a::VersionWeight, b::VersionWeight) =
VersionWeight(a.major+b.major, a.minor+b.minor, a.patch+b.patch,
a.prerelease+b.prerelease, a.build+b.build)
Base.:(-)(a::VersionWeight) =
VersionWeight(-a.major, -a.minor, -a.patch,
-a.prerelease, -a.build)
function Base.cmp(a::VersionWeight, b::VersionWeight)
c = cmp(a.major, b.major); c != 0 && return c
c = cmp(a.minor, b.minor); c != 0 && return c
c = cmp(a.patch, b.patch); c != 0 && return c
c = cmp(a.prerelease, b.prerelease); c != 0 && return c
return cmp(a.build, b.build)
end
Base.isless(a::VersionWeight, b::VersionWeight) = cmp(a,b) < 0
Base.:(==)(a::VersionWeight, b::VersionWeight) = cmp(a,b) == 0
Base.abs(a::VersionWeight) =
VersionWeight(abs(a.major), abs(a.minor), abs(a.patch),
abs(a.prerelease), abs(a.build))
Base.copy(a::VersionWeight) =
VersionWeight(a.major, a.minor, a.patch,
copy(a.prerelease), copy(a.build))
# This isn't nice, but it's for debugging only anyway
function Base.show(io::IO, a::VersionWeight)
print(io, "(", a.major)
a == VersionWeight(a.major) && @goto done
print(io, ".", a.minor)
a == VersionWeight(a.major, a.minor) && @goto done
print(io, ".", a.patch)
a.prerelease _vwprebuild_zero && print(io, "-", a.prerelease)
a.build _vwprebuild_zero && print(io, "+", a.build)
@label done
print(io, ")")
end
end