Skip to content

Commit b35fe2d

Browse files
committed
New BinaryVector enumerate with no allocation, and other improvements.
1 parent 2bad1f2 commit b35fe2d

File tree

8 files changed

+553
-300
lines changed

8 files changed

+553
-300
lines changed

binary_vector.go

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import (
44
"bytes"
55
"crypto/rand"
66
"fmt"
7-
mathrand "math/rand"
7+
"math/big"
8+
"math/bits"
89
"github.com/lukechampine/fastxor"
910
)
1011

@@ -56,9 +57,21 @@ func (v BinaryVector) Add(u BinaryVector) BinaryVector {
5657
return w
5758
}
5859

60+
// xxx test; deprecated
61+
// func (v BinaryVector) AddInPlace(u BinaryVector) {
62+
// fastxor.Bytes(v.data, v.data, u.data)
63+
// }
64+
5965
// xxx test
60-
func (v BinaryVector) AddInPlace(u BinaryVector) {
61-
fastxor.Bytes(v.data, v.data, u.data)
66+
func (v BinaryVector) Clear() {
67+
clear(v.data)
68+
}
69+
70+
// xxx test
71+
func (v BinaryVector) Clone() BinaryVector {
72+
w := NewBinaryVector(v.Length())
73+
copy(w.data, v.data)
74+
return w
6275
}
6376

6477
// xxx test
@@ -113,14 +126,21 @@ func (v BinaryVector) Project(n int, predicate func (i int) bool) BinaryVector {
113126
return pv
114127
}
115128

116-
// xxx test
117129
func (v BinaryVector) RandomizeWithWeight(weight int) {
118130
n := v.Length()
119-
for i := 0; i < n; i++ {
120-
v.Set(i, 0)
121-
}
131+
v.Clear()
122132
for i := 0; i < weight; i++ {
123-
v.Set(mathrand.Intn(n), 1)
133+
for {
134+
index, err := rand.Int(rand.Reader, big.NewInt(int64(n)))
135+
if err != nil {
136+
panic(err)
137+
}
138+
idx := int(index.Int64())
139+
if v.Get(idx) == 0 {
140+
v.Set(idx, 1)
141+
break
142+
}
143+
}
124144
}
125145
}
126146

@@ -157,6 +177,13 @@ func (v BinaryVector) String() string {
157177
return buf.String()
158178
}
159179

180+
// sets v to the sum of a and b
181+
// xxx test
182+
func (v BinaryVector) Sum(a, b BinaryVector) {
183+
fastxor.Bytes(v.data, a.data, b.data)
184+
// log.Printf("xxx Sum: %v + %v = %v", a, b, v)
185+
}
186+
160187
func (v BinaryVector) SupportString() string {
161188
var buf bytes.Buffer
162189
first := true
@@ -174,12 +201,16 @@ func (v BinaryVector) SupportString() string {
174201
return buf.String()
175202
}
176203

204+
// xxx test
205+
func (v BinaryVector) Toggle(i int) {
206+
v.Set(i, 1-v.Get(i))
207+
}
208+
209+
// xxx test
177210
func (v BinaryVector) Weight() int {
178211
weight := 0
179-
for i := 0; i < v.Length(); i++ {
180-
if v.Get(i) == 1 {
181-
weight++
182-
}
212+
for _, b := range v.data {
213+
weight += bits.OnesCount8(b)
183214
}
184215
return weight
185216
}
@@ -220,6 +251,21 @@ func AllBinaryVectors(n int) []BinaryVector {
220251
return vectors
221252
}
222253

254+
func EnumerateBinaryVectors(n int, v BinaryVector, F func() (continue_ bool) ) {
255+
if n > 63 {
256+
panic("EnumerateBinaryVectors: n > 63 not supported")
257+
}
258+
m := uint(1) << uint(n)
259+
for i := uint(0); i < m; i++ {
260+
for j := 0; j < n; j++ {
261+
v.Set(j, uint8((i >> j) & 1))
262+
}
263+
if !F() {
264+
break
265+
}
266+
}
267+
}
268+
223269
// this is partially a compatibility shim for older code
224270
// that still uses the BinaryVector type.
225271
func EnumerateBinaryVectorSpaceList(generators BinaryMatrix) []BinaryVector {
@@ -296,4 +342,3 @@ func pow(base, exp int) int {
296342
}
297343
return result
298344
}
299-

binary_vector_test.go

Lines changed: 95 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
package golsv
22

33
import (
4+
"math/rand"
45
"reflect"
56
"testing"
67
)
78

8-
func TestBinaryMatrix_BinaryVectorToMatrix(t *testing.T) {
9+
func TestBinaryVectorAdd(t *testing.T) {
10+
tests := []struct {
11+
a, b, want BinaryVector
12+
}{
13+
{a: NewBinaryVectorFromString("0000"), b: NewBinaryVectorFromString("0000"), want: NewBinaryVectorFromString("0000")},
14+
{a: NewBinaryVectorFromString("0000"), b: NewBinaryVectorFromString("0001"), want: NewBinaryVectorFromString("0001")},
15+
{a: NewBinaryVectorFromString("0001"), b: NewBinaryVectorFromString("0001"), want: NewBinaryVectorFromString("0000")},
16+
{a: NewBinaryVectorFromString("0001"), b: NewBinaryVectorFromString("1001"), want: NewBinaryVectorFromString("1000")},
17+
}
18+
for i, tt := range tests {
19+
got := tt.a.Add(tt.b)
20+
if !reflect.DeepEqual(tt.want, got) {
21+
t.Errorf("%d: got=%v want=%v", i, got, tt.want)
22+
}
23+
}
24+
}
25+
26+
func TestBinaryVectorMatrix(t *testing.T) {
927
tests := []struct {
1028
name string
1129
v BinaryVector
@@ -43,6 +61,63 @@ func TestBinaryMatrix_BinaryVectorToMatrix(t *testing.T) {
4361
}
4462
}
4563

64+
func TestBinaryVectorProject(t *testing.T) {
65+
tests := []struct {
66+
v BinaryVector
67+
proj []int
68+
want BinaryVector
69+
}{
70+
{v: NewBinaryVectorFromString("0000"), proj: []int{0, 1, 2}, want: NewBinaryVectorFromString("000")},
71+
{v: NewBinaryVectorFromString("0001"), proj: []int{0, 1, 2}, want: NewBinaryVectorFromString("000")},
72+
{v: NewBinaryVectorFromString("0011"), proj: []int{0, 1, 2}, want: NewBinaryVectorFromString("001")},
73+
{v: NewBinaryVectorFromString("1011"), proj: []int{1, 2}, want: NewBinaryVectorFromString("01")},
74+
}
75+
for i, tt := range tests {
76+
hot := make(map[int]bool)
77+
for _, j := range tt.proj {
78+
hot[j] = true
79+
}
80+
predicate := func(i int) bool {
81+
_, ok := hot[i]
82+
return ok
83+
}
84+
got := tt.v.Project(len(tt.proj), predicate)
85+
if !reflect.DeepEqual(tt.want, got) {
86+
t.Errorf("%d: got=%v want=%v", i, got, tt.want)
87+
}
88+
}
89+
}
90+
91+
func TestBinaryVectorRandomizeWithWeight(t *testing.T) {
92+
trials := 10
93+
for i := 0; i < trials; i++ {
94+
n := 100
95+
w := rand.Intn(n)
96+
v := NewBinaryVector(n)
97+
v.RandomizeWithWeight(w)
98+
if v.Weight() != w {
99+
t.Errorf("got=%d want=%d", v.Weight(), w)
100+
}
101+
}
102+
}
103+
104+
func TestBinaryVectorWeight(t *testing.T) {
105+
tests := []struct {
106+
v BinaryVector
107+
want int
108+
}{
109+
{v: NewBinaryVectorFromString("0000"), want: 0},
110+
{v: NewBinaryVectorFromString("0001"), want: 1},
111+
{v: NewBinaryVectorFromString("0011"), want: 2},
112+
}
113+
for i, tt := range tests {
114+
got := tt.v.Weight()
115+
if got != tt.want {
116+
t.Errorf("%d: got=%d want=%d", i, got, tt.want)
117+
}
118+
}
119+
}
120+
46121
func TestBinaryMatrix_LinearCombination(t *testing.T) {
47122
tests := []struct {
48123
name string
@@ -172,63 +247,26 @@ func TestAllBinaryVectors(t *testing.T) {
172247
}
173248
}
174249

175-
func TestBinaryVectorAdd(t *testing.T) {
176-
tests := []struct {
177-
a, b, want BinaryVector
178-
}{
179-
{a: NewBinaryVectorFromString("0000"), b: NewBinaryVectorFromString("0000"), want: NewBinaryVectorFromString("0000")},
180-
{a: NewBinaryVectorFromString("0000"), b: NewBinaryVectorFromString("0001"), want: NewBinaryVectorFromString("0001")},
181-
{a: NewBinaryVectorFromString("0001"), b: NewBinaryVectorFromString("0001"), want: NewBinaryVectorFromString("0000")},
182-
{a: NewBinaryVectorFromString("0001"), b: NewBinaryVectorFromString("1001"), want: NewBinaryVectorFromString("1000")},
250+
func TestEnumerateBinaryVectors(t *testing.T) {
251+
n := 3
252+
res := make([]BinaryVector, 0)
253+
buf := NewBinaryVector(n)
254+
EnumerateBinaryVectors(n, buf, func() (continue_ bool) {
255+
res = append(res, buf.Clone())
256+
return true
257+
})
258+
expected := []BinaryVector{
259+
NewBinaryVectorFromString("000"),
260+
NewBinaryVectorFromString("100"),
261+
NewBinaryVectorFromString("010"),
262+
NewBinaryVectorFromString("110"),
263+
NewBinaryVectorFromString("001"),
264+
NewBinaryVectorFromString("101"),
265+
NewBinaryVectorFromString("011"),
266+
NewBinaryVectorFromString("111"),
183267
}
184-
for i, tt := range tests {
185-
got := tt.a.Add(tt.b)
186-
if !reflect.DeepEqual(tt.want, got) {
187-
t.Errorf("%d: got=%v want=%v", i, got, tt.want)
188-
}
268+
if !reflect.DeepEqual(res, expected) {
269+
t.Errorf("got=%v want=%v", res, expected)
189270
}
190271
}
191272

192-
func NewTestBinaryVectorWeightFromInts(t *testing.T) {
193-
tests := []struct {
194-
v BinaryVector
195-
want int
196-
}{
197-
{v: NewBinaryVectorFromString("0000"), want: 0},
198-
{v: NewBinaryVectorFromString("0001"), want: 1},
199-
{v: NewBinaryVectorFromString("0011"), want: 2},
200-
}
201-
for i, tt := range tests {
202-
got := tt.v.Weight()
203-
if got != tt.want {
204-
t.Errorf("%d: got=%d want=%d", i, got, tt.want)
205-
}
206-
}
207-
}
208-
209-
func TestBinaryVectorProject(t *testing.T) {
210-
tests := []struct {
211-
v BinaryVector
212-
proj []int
213-
want BinaryVector
214-
}{
215-
{v: NewBinaryVectorFromString("0000"), proj: []int{0, 1, 2}, want: NewBinaryVectorFromString("000")},
216-
{v: NewBinaryVectorFromString("0001"), proj: []int{0, 1, 2}, want: NewBinaryVectorFromString("000")},
217-
{v: NewBinaryVectorFromString("0011"), proj: []int{0, 1, 2}, want: NewBinaryVectorFromString("001")},
218-
{v: NewBinaryVectorFromString("1011"), proj: []int{1, 2}, want: NewBinaryVectorFromString("01")},
219-
}
220-
for i, tt := range tests {
221-
hot := make(map[int]bool)
222-
for _, j := range tt.proj {
223-
hot[j] = true
224-
}
225-
predicate := func(i int) bool {
226-
_, ok := hot[i]
227-
return ok
228-
}
229-
got := tt.v.Project(len(tt.proj), predicate)
230-
if !reflect.DeepEqual(tt.want, got) {
231-
t.Errorf("%d: got=%v want=%v", i, got, tt.want)
232-
}
233-
}
234-
}

cmd/coboundary-decoder/main.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,26 @@ import (
1010
// Input:
1111
//
1212
// - the boundary matrices defining the LSV complex
13+
// - the Z_1 generators matrix
14+
// - error weight range
15+
// - number of samples per weight
16+
// - [OR] a file containing the errors to decode
1317
//
1418
// Output:
1519
//
1620
// - (log) decoding success samples
21+
// - (optional) results json file
1722
//
1823
type CoboundaryDecoderArgs struct {
1924
D1File string
2025
D2File string
26+
Z_1File string
2127
Verbose bool
2228
MinErrorWeight int
2329
MaxErrorWeight int
2430
SamplesPerWeight int
31+
ErrorsFile string
32+
ResultsFile string
2533
golsv.ProfileArgs
2634
}
2735

@@ -34,9 +42,12 @@ func parseFlags() CoboundaryDecoderArgs {
3442
args.ProfileArgs.ConfigureFlags()
3543
flag.StringVar(&args.D1File, "d1", "", "D1 matrix file")
3644
flag.StringVar(&args.D2File, "d2", "", "D2 matrix file")
45+
flag.StringVar(&args.Z_1File, "Z_1", "", "Z_1 matrix file")
3746
flag.IntVar(&args.MinErrorWeight, "min-error-weight", args.MinErrorWeight, "Minimum error weight")
3847
flag.IntVar(&args.MaxErrorWeight, "max-error-weight", args.MaxErrorWeight, "Maximum error weight")
3948
flag.IntVar(&args.SamplesPerWeight, "samples-per-weight", args.SamplesPerWeight, "Number of samples per weight")
49+
flag.StringVar(&args.ErrorsFile, "errors", args.ResultsFile, "Errors file. Decode these errors instead of sampling.")
50+
flag.StringVar(&args.ResultsFile, "results", "", "Results file")
4051
flag.BoolVar(&args.Verbose, "verbose", false, "Verbose output")
4152
flag.Parse()
4253

@@ -49,6 +60,10 @@ func parseFlags() CoboundaryDecoderArgs {
4960
fmt.Println("D2 matrix file is required")
5061
ok = false
5162
}
63+
if args.Z_1File == "" {
64+
fmt.Println("Z_1 matrix file is required")
65+
ok = false
66+
}
5267
if !ok {
5368
flag.Usage()
5469
panic("missing required arguments")
@@ -77,11 +92,37 @@ func main() {
7792
if args.Verbose {
7893
log.Printf("done; read %s", D2)
7994
}
95+
if args.Verbose {
96+
log.Printf("reading matrix Z_1 from %s", args.Z_1File)
97+
}
98+
Z_1 := golsv.ReadSparseBinaryMatrixFile(args.Z_1File)
99+
if args.Verbose {
100+
log.Printf("done; read %s", Z_1)
101+
}
80102
complex := golsv.NewZComplexFromBoundaryMatrices(D1, D2)
81103

82-
decoder := golsv.NewCoboundaryDecoder(complex, args.Verbose)
104+
decoder := golsv.NewCoboundaryDecoder(complex, Z_1, args.Verbose)
83105

84-
sampler := golsv.NewDecoderSampler(decoder, args.MinErrorWeight, args.MaxErrorWeight, args.SamplesPerWeight, args.Verbose)
85-
sampler.Run()
106+
if args.ErrorsFile != "" {
107+
decodeErrorsFromFile(decoder, args)
108+
} else {
109+
sampler := golsv.NewDecoderSampler(decoder, args.MinErrorWeight, args.MaxErrorWeight, args.SamplesPerWeight, args.ResultsFile, args.Verbose)
110+
sampler.Run()
111+
}
86112
}
87113

114+
func decodeErrorsFromFile[T any](decoder *golsv.CoboundaryDecoder[T], args CoboundaryDecoderArgs) {
115+
errors := golsv.ReadSparseBinaryMatrixFile(args.ErrorsFile)
116+
log.Printf("read %s: %s", args.ErrorsFile, errors)
117+
for j := 0; j < errors.NumColumns(); j++ {
118+
errorVec := errors.ColumnVector(j)
119+
syndrome := decoder.Syndrome(errorVec)
120+
log.Printf("decoding column %d error weight=%d syndrome weight=%d", j, errorVec.Weight(), syndrome.Weight())
121+
err, _ := decoder.Decode(syndrome)
122+
if err != nil {
123+
log.Printf("error decoding column %d: %v", j, err)
124+
} else {
125+
log.Printf("decoded column %d", j)
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)