Skip to content

Commit 5344bf9

Browse files
committed
Update weight_dist.jl
added comments to wt_dist
1 parent 3eb3685 commit 5344bf9

File tree

1 file changed

+66
-39
lines changed

1 file changed

+66
-39
lines changed

src/Classical/weight_dist.jl

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ end
419419

420420
"""
421421
minimum_distance_Gray(C::AbstractLinearCode; alg::Symbol = :Zimmermann, v::Bool = false)
422+
422423
Return the minimum distance of `C` using a deterministic algorithm based on enumerating
423424
constant weight codewords of the binary reflected Gray code. If a word of minimum weight
424425
is found before the lower and upper bounds cross, it is returned; otherwise, the zero vector
@@ -427,7 +428,7 @@ is returned.
427428
show_progress will display a progress meter for each iteration of a weight that takes longer than
428429
a second
429430
"""
430-
function minimum_distance_Gray(C::AbstractLinearCode; alg::Symbol = :auto, v::Bool = false,
431+
function minimum_distance_Gray(C::AbstractLinearCode; alg::Symbol = :auto, verbose::Bool = false,
431432
show_progress = true)
432433

433434
ord_F = Int(order(C.F))
@@ -443,39 +444,50 @@ function minimum_distance_Gray(C::AbstractLinearCode; alg::Symbol = :auto, v::Bo
443444

444445
if alg == :auto
445446
if typeof(C) <: AbstractCyclicCode
446-
v && println("Detected a cyclic code, using Chen's adaption.")
447+
verbose && println("Detected a cyclic code, using Chen's adaption.")
447448
alg = :Chen
448449
# TODO: fix this case
449450
elseif typeof(C) <: AbstractQuasiCyclicCode
450-
v && println("Detected a quasi-cyclic code, using White's adaption.")
451+
verbose && println("Detected a quasi-cyclic code, using White's adaption.")
451452
alg = :White
452453
else
453-
v && println("Using Zimmermann's algorithm.")
454+
verbose && println("Using Zimmermann's algorithm.")
454455
alg = :Zimmermann
455456
end
456457
end
458+
# TODO should never hit this case with the else in there?
457459
alg == :auto && throw(ErrorException("Could not determine minimum distance algo automatically"))
458460

459-
if alg in (:Brouwer, :Zimmermann)
460-
return _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg = alg, verbose = v, show_progress = show_progress)
461+
# really this should work for all just the same?
462+
if alg in (:Brouwer, :Zimmermann)
463+
return _minimum_distance_BZ(C::AbstractLinearCode, alg, verbose, show_progress)
461464
end
462465
println("Warning: old enumeration algorithm selected. Performance will be slow") # TODO remove when all code updated
463466
return _minimum_distance_enumeration_with_matrix_multiply(C::AbstractLinearCode; info_set_alg = alg)
464467
end
465468

466-
function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zimmermann,
467-
verbose::Bool = false, dbg = Dict(), show_progress=false)
469+
# this is a private function, so there's no point in having keyword arguments
470+
function _minimum_distance_BZ(C::AbstractLinearCode, info_set_alg::Symbol, verbose::Bool,
471+
show_progress::Bool)
472+
473+
# you never pass this in anywhere so moving out
474+
dbg::Dict{String, Int} = Dict()
468475
dbg_key_exit_r = "exit_r"
469476

470-
ord_F = Int(order(C.F))
471-
ord_F == 2 || throw(ArgumentError("Currently only implemented for binary codes."))
477+
# removing since checked in main function
478+
# ord_F = Int(order(C.F))
479+
# ord_F == 2 || throw(ArgumentError("Currently only implemented for binary codes."))
480+
# put this at user entry point, although if 2^k is large, 2^(n - k) maybe small enough to do
481+
# so we should put this in an if statement above if we are doing G or H
472482
C.k < 2^16 || throw(DomainError("The given linear code has length k >= 2^16 which is not supported"))
483+
473484
info_set_alg (:Brouwer, :Zimmermann) || throw(ArgumentError("Unknown information set algorithm. Expected `:Brouwer`, `:Zimmermann`"))
474485

475486
generator_matrix(C, true) # ensure G_stand exists
476487
if _has_empty_vec(C.G, :cols)
477488
#TODO err string can instruct the user to construct a new code without 0 cols and tell them the function for that
478-
throw(ArgumentError("Codes with standard form of generator matrix having 0 columns not supported"))
489+
throw(ArgumentError("Codes with standard form of generator matrix having 0 columns not supported"))
490+
# is that actually possible? I thought we remove this in the linear code constructor? If we don't we should and then remove this here
479491
end
480492
# generate if not pre-stored
481493
parity_check_matrix(C)
@@ -494,20 +506,22 @@ function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zim
494506
end
495507

496508
if haskey(dbg, dbg_key_exit_r)
497-
verbose && println("dbg Dict: largest message weight searched stored @key=$dbg_key_exit_r")
509+
verbose && println("dbg Dict: largest message weight searched stored @key = $dbg_key_exit_r")
498510
dbg[dbg_key_exit_r] = -1
499511
end
500512

501513
k, n = size(C.G)
502-
A_mats_trunc = [Matrix{UInt16}(undef, k, n-k) for _ in 1:length(A_mats)]
503-
for i in 1:size(A_mats, 1)
504-
A_mats_trunc[i] = deepcopy(A_mats[i][k+1 : n, :])
514+
A_mats_trunc = [Matrix{UInt16}(undef, k, n - k) for _ in 1:length(A_mats)]
515+
for i in 1:size(A_mats, 1)
516+
A_mats_trunc[i] = deepcopy(A_mats[i][k + 1 : n, :])
505517
end
506518

519+
# I don't remember this case in the paper
507520
if info_set_alg == :Brouwer && rnks[h] != k
508521
println("Rank of last matrix too small")
509522
return
510523
end
524+
# I think we have to remove this from the verbose statement to work
511525
if verbose
512526
print("Generated $h information sets with ranks: ")
513527
for i in 1:h
@@ -532,10 +546,10 @@ function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zim
532546
(!triply_even_flag && !doubly_even_flag && even_flag) && println("Detected an even code.")
533547
end
534548

535-
# initial_perm_ind will match the permutation we use for the 'found' vector if the found vector is nonzero. To simplify the code below we're going to choose an initial permutation arbitrarily.
536-
initial_perm_ind = 1
537-
# following loop is the r=1 case of the enumeration. We do this case here because we want to make a good guess at the terminating r before we start multiple threads
538-
for (j, g) in enumerate(A_mats) # loop over the A_mats rather than the original G because it would add another case to deal with later
549+
# initial_perm_ind will match the permutation we use for the 'found' vector if the found vector is nonzero. To simplify the code below we're going to choose an initial permutation arbitrarily.
550+
initial_perm_ind = 1
551+
# following loop is the r = 1 case of the enumeration. We do this case here because we want to make a good guess at the terminating r before we start multiple threads
552+
for (j, g) in enumerate(A_mats) # loop over the A_mats rather than the original G because it would add another case to deal with later
539553
# can make this faster with dots and views
540554
w, i = _min_wt_col(g)
541555
if w <= C.u_bound
@@ -546,13 +560,15 @@ function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zim
546560
end
547561

548562
verbose && println("Current upper bound: $(C.u_bound)")
563+
# I'm actually surpised this works since Oscar now requires [:, 1:1] for most things
549564
found = A_mats[1][:, 1]
550565

551566
l = 0
552-
if verbose
567+
# not entirely sure what's going on here since it's never used later, I guess just for testing, but if we want it printed it never ends up printed with the current comments
568+
if verbose
553569
_, _, b_rnks = information_sets(C.G, :Brouwer, permute = true, only_A = false)
554570
b_h = length(b_rnks)
555-
b_lower_bounds = [information_set_lower_bound(r+1, n, k, l, [k - 0 for i in 1:b_h], :Brouwer, even = even_flag, doubly_even = doubly_even_flag, triply_even = triply_even_flag) for r in 1:k-1]
571+
b_lower_bounds = [information_set_lower_bound(r + 1, n, k, l, [k - 0 for i in 1:b_h], :Brouwer, even = even_flag, doubly_even = doubly_even_flag, triply_even = triply_even_flag) for r in 1:k - 1]
556572
b_r_term = findfirst(x -> x C.u_bound, b_lower_bounds)
557573

558574
# _, _, z_rnks = information_sets(G, :Zimmermann, permute = true, only_A = false)
@@ -563,38 +579,41 @@ function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zim
563579
# verbose && println("Predicted termination weight based on current upper bound: Brouwer $b_r_term Zimm $z_r_term")
564580
end
565581

566-
#Note the r+1 here.
567-
lower_bounds_for_prediction = [information_set_lower_bound(r+1, n, k, l, rank_defs, info_set_alg, even = even_flag, doubly_even = doubly_even_flag, triply_even = triply_even_flag) for r in 1:k-1]
582+
# Note the r + 1 here
583+
lower_bounds_for_prediction = [information_set_lower_bound(r + 1, n, k, l, rank_defs, info_set_alg, even = even_flag, doubly_even = doubly_even_flag, triply_even = triply_even_flag) for r in 1:k - 1]
568584
r_term = findfirst(x -> x C.u_bound, lower_bounds_for_prediction)
569585
if isnothing(r_term)
570-
raise(DomainError("invalid termination r"))
586+
raise(DomainError("Invalid termination r"))
571587
end
572588
verbose && println("Predicted termination weight based on current upper bound: $r_term")
573589

574-
#In the main loop we check if lower bound > upper bound before we enumerate and so the lower bounds for the loop use r not r+1
575-
lower_bounds = [information_set_lower_bound(r, n, k, l, rank_defs, info_set_alg, even = even_flag, doubly_even = doubly_even_flag, triply_even = triply_even_flag) for r in 1:k-1]
590+
# In the main loop we check if lower bound > upper bound before we enumerate and so the lower bounds for the loop use r not r + 1
591+
lower_bounds = [information_set_lower_bound(r, n, k, l, rank_defs, info_set_alg, even = even_flag, doubly_even = doubly_even_flag, triply_even = triply_even_flag) for r in 1:k - 1]
576592

577593
predicted_work_factor = fld(n, k) * sum([binomial(k, i) for i in 1:r_term])
578594
verbose && println("Predicted work factor: $predicted_work_factor")
579595
if show_progress
580-
prog_bar = Progress(predicted_work_factor, dt=1.0, showspeed=true) # updates no faster than once every 1s
596+
prog_bar = Progress(predicted_work_factor, dt = 1.0, showspeed = true) # updates no faster than once every 1s
581597
end
582-
weight_sum_bound = min(2 * C.u_bound + 5, n-k)
598+
# don't understand this but okay
599+
weight_sum_bound = min(2 * C.u_bound + 5, n - k)
583600
verbose && println("Codeword weights initially checked on first $weight_sum_bound entries")
584601

585602
num_thrds = Threads.nthreads()
586603
verbose && println("Number of threads ", num_thrds)
587604
for r in 2:k
605+
# TODO this case should never happen given the error above?
588606
if r > 2^16
589607
verbose && println("Warning: Reached an r larger than 2^16")
590608
end
591609
C.l_bound < lower_bounds[r] && (C.l_bound = lower_bounds[r];)
610+
# I assume we want to uncomment these?
592611
# an even code can't have have an odd minimum weight
593612
# (!triply_even_flag && !doubly_even_flag && even_flag) && (C.l_bound += C.l_bound % 2;)
594613
# (!triply_even_flag && doubly_even_flag) && (C.l_bound += 4 - C.l_bound % 4;)
595614
# triply_even_flag && (C.l_bound += 8 - C.l_bound % 8;)
596615
if C.l_bound >= C.u_bound
597-
dbg[dbg_key_exit_r] = r-1
616+
dbg[dbg_key_exit_r] = r - 1
598617
break
599618
end
600619
verbose && println("r: $r")
@@ -608,6 +627,7 @@ function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zim
608627
end
609628
i_count > 0 && println("$i_count of the original $h information sets no longer contribute to the lower bound")
610629
end
630+
# wait... does this work for nonbinary? cause we do throw an error above if it's not binary
611631
p = Int(characteristic(C.F))
612632

613633
uppers = [C.u_bound for _ in 1:num_thrds]
@@ -630,17 +650,19 @@ function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zim
630650
first_vec[i] = 1
631651
end
632652
else
653+
# we shouldn't need the CT. here anymore
633654
CodingTheory._subset_unrank_to_vec!(init_rank, UInt64(r), first_vec)
634655
end
635656

636-
# as in White Algo 7.1 we loop over matrices first
657+
# I couldn't figure out why we want to do this but sure
658+
# as in White Algo 7.1 we loop over matrices first
637659
for i in 1:h
638660
if keep_going[] == false
639661
verbose && println(thrd_stop_msg)
640662
break
641663
end
642664

643-
c_itr = zeros(UInt16, C.n - C.k)
665+
c_itr = zeros(UInt16, C.n - C.k)
644666
is_first = true
645667
curr_mat = A_mats_trunc[i]
646668
count = UInt128(0)
@@ -650,11 +672,12 @@ function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zim
650672
println(thrd_stop_msg)
651673
break
652674
end
653-
show_progress && ProgressMeter.next!(prog_bar)
675+
show_progress && ProgressMeter.next!(prog_bar)
676+
654677
if r - rank_defs[i] > 0
655678
if is_first
656679
LinearAlgebra.mul!(c_itr, curr_mat, first_vec)
657-
@inbounds @simd for j in eachindex(c_itr)
680+
@inbounds @simd for j in eachindex(c_itr)
658681
c_itr[j] %= p
659682
end
660683
is_first = false
@@ -675,41 +698,45 @@ function _minimum_distance_BZ(C::AbstractLinearCode; info_set_alg::Symbol = :Zim
675698
verbose && @assert w != 0
676699
if uppers[ind] > w
677700
subset_vec_full = zeros(Int, k)
701+
# CT.
678702
CodingTheory._subset_unrank_to_vec!(UInt128(init_rank + count), UInt64(r), subset_vec_full)
679703

680704
uppers[ind] = w
681705
founds[ind] = vcat(subset_vec_full, c_itr)
682-
verbose && @assert size(founds[ind], 1) == C.n "found vector has length $(size(founds[ind], 1)) but should be n=$(C.n)"
683-
exit_thread_indicator_vec[ind] = i
706+
# is it possible to hit this assert if programmed correctly and checked on one or two runs?
707+
verbose && @assert size(founds[ind], 1) == C.n "found vector has length $(size(founds[ind], 1)) but should be n = $(C.n)"
708+
exit_thread_indicator_vec[ind] = i
684709

685-
println("Adjusting (local) upper bound: $w for c_itr=$(Int.(c_itr))")
710+
println("Adjusting (local) upper bound: $w for c_itr = $(Int.(c_itr))")
686711
if C.l_bound == uppers[ind]
687-
println("early exit")
712+
verbose && println("Early exit")
688713
Threads.atomic_cas!(keep_going, true, false)
689714
else
690715
r_term = findfirst(x -> x C.u_bound, lower_bounds)
691716
isnothing(r_term) && (r_term = k;)
692717
verbose && println("Updated termination weight: $r_term")
718+
# can we update the progress meter size here or just maybe add an amount equal to the amount now skipped?
693719
end
694720
end
695721
end
696722
end
697723
count = add!(count, count, 1)
698724
end
699725
end
700-
loc = argmin(uppers)
726+
loc = argmin(uppers)
701727
C.u_bound = uppers[loc]
702728
found = founds[loc]
703729
initial_perm_ind = exit_thread_indicator_vec[loc]
704730
end
705731
end
706732

707733
C.d = C.u_bound
734+
# why would this not always be the proper wt?
708735
y = matrix(C.F, 1, n, perms_mats[initial_perm_ind] * found) # weight(y) >= C.d, with equality not being the typical case
709736
verbose && @assert iszero(C.H * transpose(y))
710737
if dbg[dbg_key_exit_r] == -1
711738
dbg[dbg_key_exit_r] = r
712-
end
739+
end
713740
show_progress && ProgressMeter.finish!(prog_bar)
714741
verbose && println("Computation complete")
715742
return C.u_bound, y

0 commit comments

Comments
 (0)