diff --git a/doc/body.autodoc b/doc/body.autodoc index 7f5538e..4039f9d 100644 --- a/doc/body.autodoc +++ b/doc/body.autodoc @@ -436,7 +436,7 @@ storage options for elements of an extension field can in be used. $a_0+a_1x+a_2x^2+\ldots +a_{m-1}x^{m-1}$ as digits of a $p$-ary integer $(a_{m-1}a_{m-2}\ldots a_2a_1)_p$. (**this is currently not implemented**) - + @Subsection Matrix storage format There are two recommended storage formats. @@ -593,7 +593,7 @@ i j element[i,j] @EndCode @InsertCode MMXFormatLineB -* `type=complex`: +* `type=complex`: @BeginCode MMXFormatLineC i j a[i,j] b[i,j] @EndCode @@ -649,7 +649,7 @@ is specified explicitly, this matrix would work with any prime field. @BeginCode SampleFileA -%%MatrixMarket matrix coordinate integer general +%%MatrixMarket matrix coordinate integer general % Field: GF(7) % 5-qubit code generator matrix / normal storage with intercalated cols 5 10 20 @@ -679,7 +679,7 @@ prime field. This same matrix is stored in the file `matrices/n5k1A.mtx`. This is how the matrix can be read and distance calculated: @BeginExample - filedir:=DirectoriesPackageLibrary("QDistRnd","matrices");; + filedir:=DirectoriesPackageLibrary("QDistRnd","matrices");; lis:=ReadMTXE(Filename(filedir,"n5k1A.mtx" ));; Print("field ",lis[1],"\n"); #! field GF(7) diff --git a/examples/examples.g b/examples/examples.g index 8171e11..34aa7e5 100644 --- a/examples/examples.g +++ b/examples/examples.g @@ -1,12 +1,14 @@ #! @Chapter Examples #! @Section The 5-qubit code -#! Generate the matrix of the 5-qubit code over GF(3) with the stabilizer group +#! In this example, we generate the matrix of the 5-qubit code over GF(3) with +#! the stabilizer group #! generated by cyclic shifts of the operator $X_0Z_1 \bar Z_2 \bar #! X_3$ which corresponds to the polynomial $h(x)=1+x^3-x^5-x^6$ #! (a factor $X_i^a$ corresponds to a monomial $a x^{2i}$, and a #! factor $Z_i^b$ to a monomial $b x^{2i+1}$), -#! calculate the distance, and save into the file. +#! calculate the distance, save into a file using the function `WriteMTXE()`, and read the file back in +#! using the function `ReadMTXE()`. #! @BeginExample q:=3;; F:=GF(q);; x:=Indeterminate(F,"x");; poly:=One(F)*(1+x^3-x^5-x^6);; @@ -19,14 +21,39 @@ Display(mat); #! . 2 2 . . . 1 . . 1 d:=DistRandStab(mat,100,1,0 : field:=F,maxav:=20/n); #! 3 -WriteMTXE("matrices/n5_q3_complex.mtx",3,mat, +tmp_file_name:=Filename(DirectoryTemporary(),"n5_q3_complex.mtx");; +WriteMTXE(tmp_file_name,3,mat, "% The 5-qubit code [[5,1,3]]_3", "% Generated from h(x)=1+x^3-x^5-x^6", - "% Example from the QDistRnd GAP package" : field:=F); -#! File matrices/n5_q3_complex.mtx was created + "% Example from the QDistRnd GAP package" : field:=F);; +lis:=ReadMTXE(tmp_file_name);; # Filename(filedir,"n5_q3_complex.mtx") +lis[1]; # the field +#! GF(3) +lis[2]; # converted to `pair=1` +#! 1 +Display(lis[3]); +#! 1 . . 1 . 2 2 . . . +#! . . 1 . . 1 . 2 2 . +#! 2 . . . 1 . . 1 . 2 +#! . 2 2 . . . 1 . . 1 #! @EndExample - -#! Here is the contents of the resulting file which also illustrates +#! The function `WriteMTXE()` takes several arguments which specify the details of the output file format +#! and the optional comments, see Section for the details. +#! These ensure that all information about the code is written into the file, so that for +#! reading with the function `ReadMTXE()` only the file name is needed. +#! Output is a list: `[field,pair,matrix,(list of comments)]`, where the `pair` parameter describes +#! the ordering of columns in the matrix, see . +#! Notice that a `pair=2` or `pair=3` matrix is always converted to `pair=1`, i.e., with $2n$ +#! intercalated columns $(a_1,b_1,a_2,b_2,\ldots)$. +#! The remaining portion is the list of comments. Notice that the 1st +#! and the last comment lines have been added automatically. +#! @BeginLog +#! gap> lis[4]; +#! [ "% Field: GF(3)", "% The 5-qubit code [[5,1,3]]_3", +#! "% Generated from h(x)=1+x^3-x^5-x^6", +#! "% Example from the QDistRnd GAP package", "% Values Z(3) are given" ] +#! @EndLog +#! Here is the contents of the created file which illustrates #! the `coordinate complex` data format. Here a pair $(a_{i,j},b_{i,j})$ #! in row $i$ and column $j$ is written as a row of 4 integers, "$i$ $j$ $a_{i,j}$ #! $b_{i,j}$", e.g., "1 2 0 1" @@ -58,33 +85,6 @@ WriteMTXE("matrices/n5_q3_complex.mtx",3,mat, #! 4 5 0 1 #! @EndLog -#! And now let us read the matrix back from the file using the function `ReadMTXE`. In the simplest -#! case, only the file name is needed. -#! Output is a list: `[field,pair,matrix,(list of comments)]`, where the `pair` parameter describes -#! the ordering of columns in the matrix, see . -#! Notice that a `pair=2` or `pair=3` matrix is always converted to `pair=1`, i.e., with $2n$ -#! intercalated columns $(a_1,b_1,a_2,b_2,\ldots)$. -#! @BeginExample -lis:=ReadMTXE("matrices/n5_q3_complex.mtx");; -lis[1]; # the field -#! GF(3) -lis[2]; # converted to `pair=1` -#! 1 -Display(lis[3]); -#! 1 . . 1 . 2 2 . . . -#! . . 1 . . 1 . 2 2 . -#! 2 . . . 1 . . 1 . 2 -#! . 2 2 . . . 1 . . 1 -#! @EndExample -#! The remaining portion is the list of comments. Notice that the 1st -#! and the last comment lines have been added automatically. -#! @BeginLog -#! gap> lis[4]; -#! [ "% Field: GF(3)", "% The 5-qubit code [[5,1,3]]_3", -#! "% Generated from h(x)=1+x^3-x^5-x^6", -#! "% Example from the QDistRnd GAP package", "% Values Z(3) are given" ] -#! @EndLog - #! @Section Hyperbolic codes from a file #! Here we read two CSS matrices from two different files which diff --git a/lib/qdistrnd.g b/lib/qdistrnd.g index 037c088..b5ff6f2 100644 --- a/lib/qdistrnd.g +++ b/lib/qdistrnd.g @@ -42,12 +42,12 @@ BindGlobal("QDR_SymplVecWeight", len := Length(vec); # if (not IsInt(len / 2)) then - # Error(" symplectic vector must have even length, = ", len,"\n"); + # Error(" symplectic vector must have even length, = ", len,"\n"); # fi; wgt := 0; for i in [1..(len/2)] do - if (vec[2*i-1] <> Zero(F) or vec[2*i] <> Zero(F)) then + if (vec[2*i-1] <> Zero(F) or vec[2*i] <> Zero(F)) then wgt := wgt + 1;; fi; od; @@ -472,7 +472,7 @@ BindGlobal("ReadMTXE", # fmt - array returned by QDR_ProcessFieldHeader # pair - 0,1,2 (integer) or 3 (complex), see input variables # indicates if we store matrix in the compressed form, - # using complex representation a+i*b + # using complex representation a+i*b # (normal form if pair=integer and compressed form if pair=complex), # F - Galois field, over which we are working # rowsG - number of rows in G @@ -528,14 +528,14 @@ BindGlobal("ReadMTXE", # search for the end of top comment section (including any empty lines): iComment := iCommentStart; while Length(data[iComment + 1]) = 0 or data[iComment + 1, 1] = '%' do - iComment := iComment + 1; + iComment := iComment + 1; if Length(data[iComment]) = 0 then data[iComment]:="%"; # suppress empty comment lines fi; od; for i in [iComment+1..Length(data)] do - data[i] := SplitString(data[i], " ");; # separate into records + data[i] := SplitString(data[i], " ");; # separate into records od; # Parameters (dimensions and number of non-zero elemments) of G @@ -546,14 +546,14 @@ BindGlobal("ReadMTXE", G := NullMat(rowsG, colsG, F);; # empty matrix # Then we fill G with the elements from data (no more empty / comment lines allowed) - if (pair <>3 ) then - for i in [(iComment + 2)..Length(data)] do - G[Int(data[i,1]), Int(data[i,2])] := + if (pair <>3 ) then + for i in [(iComment + 2)..Length(data)] do + G[Int(data[i,1]), Int(data[i,2])] := QDR_ProcEntry(data[i,3],fmt,StrPath,i); - od; - else # pair=3 + od; + else # pair=3 for i in [(iComment + 2)..Length(data)] do - G[Int(data[i,1]),2*Int(data[i,2])-1]:= + G[Int(data[i,1]),2*Int(data[i,2])-1]:= QDR_ProcEntry(data[i,3],fmt,StrPath,i); G[Int(data[i,1]),2*Int(data[i,2])]:= QDR_ProcEntry(data[i,4],fmt,StrPath,i); @@ -629,7 +629,7 @@ BindGlobal("WriteMTXE", # function (StrPath,pair,G,comment...) # We check pair parameter if (pair <0 ) or (pair>3) or (pair=2) then - Error("\n", "Parameter pair=",pair," not supported, must be in {0,1,3}", "\n"); + Error("\n", "Parameter pair=",pair," not supported, must be in {0,1,3}", "\n"); fi; # full file name with extension @@ -647,9 +647,9 @@ BindGlobal("WriteMTXE", # function (StrPath,pair,G,comment...) for i in [1..Length(comment)] do if comment[i,1]<>'%' then - AppendTo(filename, "% ", comment[i], "\n"); + AppendTo(filename, "% ", comment[i], "\n"); else - AppendTo(filename, comment[i], "\n"); + AppendTo(filename, comment[i], "\n"); fi; od; if IsPrime(Size(F)) then @@ -666,13 +666,13 @@ BindGlobal("WriteMTXE", # function (StrPath,pair,G,comment...) # count non-zero elements depending on the 'pair' parameter nonzero := 0; if (pair = 3) then - for i in [1..rows] do + for i in [1..rows] do nonzero := nonzero + QDR_SymplVecWeight(G[i], F);; od; else - for i in [1..rows] do - nonzero := nonzero + WeightVecFFE(G[i]);; - od; + for i in [1..rows] do + nonzero := nonzero + WeightVecFFE(G[i]);; + od; fi; if (pair < 3) then @@ -681,17 +681,17 @@ BindGlobal("WriteMTXE", # function (StrPath,pair,G,comment...) # Finally, write nonzero elements and their positions according to pair parameter and field F. if IsPrime(Size(F)) then # this includes binary field - for i in [1..rows] do - row := G[i];; + for i in [1..rows] do + row := G[i];; - pos := PositionNonZero(row, 0);; - while pos <= cols do + pos := PositionNonZero(row, 0);; + while pos <= cols do AppendTo(filename, i, " ", pos, " ", Int(row[pos]), "\n"); pos := PositionNonZero(row, pos);; - od; - od; - else # extension field - for i in [1..rows] do + od; + od; + else # extension field + for i in [1..rows] do row := G[i];; pos := PositionNonZero(row, 0);; @@ -701,29 +701,29 @@ BindGlobal("WriteMTXE", # function (StrPath,pair,G,comment...) pos := PositionNonZero(row, pos);; od; od; - fi; + fi; else # pair=3 # write dimensions of the matrix and number of line containing nonzero elements AppendTo(filename, rows, " ", cols/2," ", nonzero, "\n"); # Finally, write nonzero elements and their positions according to pair parameter and field F. - if IsPrime(Size(F)) then - for i in [1..rows] do + if IsPrime(Size(F)) then + for i in [1..rows] do row := G[i];; - pos := PositionNonZero(row, 0);; - while pos <= cols do - # For Ai = 0 - if IsInt(pos/2) then - AppendTo(filename, i, " ", pos/2, " ", 0, " ", Int(row[pos]), "\n"); - pos := PositionNonZero(row, pos);; - # For Ai != 0 + pos := PositionNonZero(row, 0);; + while pos <= cols do + # For Ai = 0 + if IsInt(pos/2) then + AppendTo(filename, i, " ", pos/2, " ", 0, " ", Int(row[pos]), "\n"); + pos := PositionNonZero(row, pos);; + # For Ai != 0 else - AppendTo(filename, i, " ", (pos+1)/2, " ", Int(row[pos]), " ", Int(row[pos + 1]), "\n"); - pos := PositionNonZero(row, pos + 1);; - fi; + AppendTo(filename, i, " ", (pos+1)/2, " ", Int(row[pos]), " ", Int(row[pos + 1]), "\n"); + pos := PositionNonZero(row, pos + 1);; + fi; od; od; else # extension field - for i in [1..rows] do + for i in [1..rows] do row := G[i];; pos := PositionNonZero(row, 0);; @@ -743,13 +743,12 @@ BindGlobal("WriteMTXE", # function (StrPath,pair,G,comment...) fi; pos := PositionNonZero(row, pos + 1);; - fi; - od; + fi; + od; od; fi; fi; - - Print("File ", filename, " was created\n"); + # Print("File ", filename, " was created\n"); end ); @@ -776,7 +775,7 @@ BindGlobal("QDR_MakeH", # Checking that G has even number of columns if (Gcd(dims[2] , 2) = 1) then - Error("\n", "Generator Matrix G has odd number of columns!", "\n"); + Error("\n", "Generator Matrix G has odd number of columns!", "\n"); fi; # Introducing check matrix @@ -867,11 +866,11 @@ BindGlobal("DistRandCSS", if debug[2]=1 then # Check that H*c = 0 if (WeightVecFFE(GX * TempVec) > 0) then Print("\nError: codeword found is not orthogonal to rows of HX!\n"); - if (colsWZ <= 100) then - Print("The improper vector is:\n"); - Display(TempVec); + if (colsWZ <= 100) then + Print("The improper vector is:\n"); + Display(TempVec); fi; - Error("\n"); + Error("\n"); fi; fi; @@ -976,7 +975,7 @@ BindGlobal("DistRandStab", function(G,num,mindist,opt...) # supported options: field, maxav local F, debug, CodeWords, mult, TempPos, dims, H, i, l, j, W, V, dimsW, rows, cols, DistBound, FirstVecFound, VecCount, per, W1, W2, TempVec, TempWeight,maxav, - per1, per2; + per1, per2; # CodeWords - if debug[4] = 1, record the first 100 different CWs with the lowest weight found so far # mult - number of times codewords from CodeWords were found # TempPos - temporary variable corresponding to the position of TempVec in CodeWords @@ -1023,9 +1022,9 @@ BindGlobal("DistRandStab", # optionally check for orthogonality if (debug[2] = 1) then - if QDR_WeightMat(G * TransposedMat(H))>0 then - Error("\n", "Problem with ortogonality GH^T!", "\n"); - fi; + if QDR_WeightMat(G * TransposedMat(H))>0 then + Error("\n", "Problem with ortogonality GH^T!", "\n"); + fi; fi; # Below we are getting vector spaces W and V ortogonal to the columns of H and G correspondingly. @@ -1042,13 +1041,13 @@ BindGlobal("DistRandStab", # The main part of algorithm. for i in [1..num] do #Print(i); - ## We start by creating random permutation for columns in W. - # per1 := ListPerm(Random(SymmetricGroup(cols/2)), cols/2);; # random permutation of length cols/2 - # per2 := []; # We extend the permutation, so it works now on pairs - # for l in [1..cols/2] do - # Append(per2, [2*per1[l]-1, 2*per1[l]]); # per2 contains the permutation we want as a list - # od; - # per := PermList(per2); # this is a permutation of length 2n moving pairs + ## We start by creating random permutation for columns in W. + # per1 := ListPerm(Random(SymmetricGroup(cols/2)), cols/2);; # random permutation of length cols/2 + # per2 := []; # We extend the permutation, so it works now on pairs + # for l in [1..cols/2] do + # Append(per2, [2*per1[l]-1, 2*per1[l]]); # per2 contains the permutation we want as a list + # od; + # per := PermList(per2); # this is a permutation of length 2n moving pairs per := Random(SymmetricGroup(cols));; @@ -1057,22 +1056,22 @@ BindGlobal("DistRandStab", W2 := PermutedCols(W2,Inverse(per));; # Inverse permutation for j in [1..rows] do - # We take one of the sample vectors for this iteration. It supposed to be low-weight. + # We take one of the sample vectors for this iteration. It supposed to be low-weight. TempVec := W2[j];; TempWeight := QDR_SymplVecWeight(TempVec, F);; - # check if this vector is a logical operator). - # First, rough check: + # check if this vector is a logical operator). + # First, rough check: if (TempWeight > 0) and (TempWeight <= DistBound) then if (WeightVecFFE(V * TempVec) > 0) then # linear independence from rows of G if debug[2]=1 then # Check that H*c = 0 if (WeightVecFFE(H * TempVec) > 0) then Print("\nSomething wrong: cw found is not orthogonal to rows of H!\n"); - if (Length(TempVec) <= 100) then - Print("The improper vector is:\n"); - Display(TempVec); - fi; - Error("\n"); + if (Length(TempVec) <= 100) then + Print("The improper vector is:\n"); + Display(TempVec); + fi; + Error("\n"); fi; fi; @@ -1081,38 +1080,38 @@ BindGlobal("DistRandStab", DistBound := TempWeight;; # min weight found VecCount := 1;; # reset the overall count of vectors of such weight - # Recording all discovered codewords of minimum weight and their multiplicities + # Recording all discovered codewords of minimum weight and their multiplicities if debug[4] = 1 or ValueOption("maxav")<>fail then - CodeWords := [TempVec];; - mult := [1];; + CodeWords := [TempVec];; + mult := [1];; fi; if debug[1] = 1 then FirstVecFound := TempVec;; fi; - # If we already received such a weight (up to now - it is minimal), + # If we already received such a weight (up to now - it is minimal), # we want to update number of vectors, corresponding to it elif (TempWeight = DistBound) then VecCount := VecCount + 1;; - # Recording of the first 100 different discovered codewords of + # Recording of the first 100 different discovered codewords of # minimum weight with their multiplicities if debug[4] = 1 or ValueOption("maxav")<>fail then TempPos := Position(CodeWords, TempVec); - if ((TempPos = fail) and (Length(mult) < 100)) then - Add(CodeWords, TempVec); - Add(mult, 1); - elif (TempPos <> fail) then - mult[TempPos] := mult[TempPos] + 1;; - fi; - fi; + if ((TempPos = fail) and (Length(mult) < 100)) then + Add(CodeWords, TempVec); + Add(mult, 1); + elif (TempPos <> fail) then + mult[TempPos] := mult[TempPos] + 1;; + fi; + fi; fi; - # Specific terminator, if we don't care for distances below a particular value. + # Specific terminator, if we don't care for distances below a particular value. if (DistBound <= mindist) then # not interesting, exit immediately! if debug[1]=1 then - Print("\n", "The found distance ",DistBound,"<=",mindist, + Print("\n", "The found distance ",DistBound,"<=",mindist, " too small, exiting!\n"); fi; return -DistBound; diff --git a/tst/qdistrnd01.tst b/tst/qdistrnd01.tst index 2683b34..48930f7 100644 --- a/tst/qdistrnd01.tst +++ b/tst/qdistrnd01.tst @@ -10,7 +10,7 @@ # gap> START_TEST("qdistrnd01.tst"); -# doc/_Chapter_Examples.xml:20-37 +# doc/_Chapter_Examples.xml:22-49 gap> q:=3;; F:=GF(q);; gap> x:=Indeterminate(F,"x");; poly:=One(F)*(1+x^3-x^5-x^6);; gap> n:=5;; @@ -22,14 +22,12 @@ gap> Display(mat); . 2 2 . . . 1 . . 1 gap> d:=DistRandStab(mat,100,1,0 : field:=F,maxav:=20/n); 3 -gap> WriteMTXE("matrices/n5_q3_complex.mtx",3,mat, +gap> tmp_file_name:=Filename(DirectoryTemporary(),"n5_q3_complex.mtx");; +gap> WriteMTXE(tmp_file_name,3,mat, > "% The 5-qubit code [[5,1,3]]_3", > "% Generated from h(x)=1+x^3-x^5-x^6", -> "% Example from the QDistRnd GAP package" : field:=F); -File matrices/n5_q3_complex.mtx was created - -# doc/_Chapter_Examples.xml:79-90 -gap> lis:=ReadMTXE("matrices/n5_q3_complex.mtx");; +> "% Example from the QDistRnd GAP package" : field:=F);; +gap> lis:=ReadMTXE(tmp_file_name);; # Filename(filedir,"n5_q3_complex.mtx") gap> lis[1]; # the field GF(3) gap> lis[2]; # converted to `pair=1` diff --git a/tst/qdistrnd03.tst b/tst/qdistrnd03.tst index af4c215..c398720 100644 --- a/tst/qdistrnd03.tst +++ b/tst/qdistrnd03.tst @@ -11,7 +11,7 @@ gap> START_TEST("qdistrnd03.tst"); # doc/_Chapter_FileFormat.xml:344-351 -gap> filedir:=DirectoriesPackageLibrary("QDistRnd","matrices");; +gap> filedir:=DirectoriesPackageLibrary("QDistRnd","matrices");; gap> lis:=ReadMTXE(Filename(filedir,"n5k1A.mtx" ));; gap> Print("field ",lis[1],"\n"); field GF(7)