Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
541 lines (419 sloc) 20.4 KB
(*
RSparseMatrix Mathematica package
Copyright (C) 2015 Anton Antonov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Written by Anton Antonov,
antononcube @ gmail.com,
Windermere, Florida, USA.
*)
(*
Mathematica is (C) Copyright 1988-2018 Wolfram Research, Inc.
Protected by copyright law and international treaties.
Unauthorized reproduction or distribution subject to severe civil
and criminal penalties.
Mathematica is a registered trademark of Wolfram Research, Inc.
*)
(* :Title: RSparseMatrix *)
(* :Author: Anton Antonov *)
(* :Date: 2015-09-27 *)
(* :Package Version: 0.6 *)
(* :Mathematica Version: 10.2 *)
(* :Copyright: (c) 2015 Anton Antonov *)
(* :Keywords: R, sparse array, sparse matrix, named rows, named columns *)
(* :Discussion:
THESE IMPLEMENTATIONS (PACKAGE) ARE OBSOLETE. Please use SSparseMatrix.m:
https://github.com/antononcube/MathematicaForPrediction/blob/master/SSparseMatrix.m
This notebook has the function implementations for manipulating objects with head RSparseMatrix that behave like
SparseArray objects but have the added functionalities to use row names and column names in a manner similar to
that of the sparse arrays objects from the base library Matrix [2] for the programming language R [1].
The idea is fairly simple: we can use associations or replacement rules to map row names and column names into integers.
Similarly to how it is done in R, RSparseMatrix handles only strings as row names and column names.
Note that assignment (with Set[__]) is not implemented.
See the commented out delegation to SparseArray implementation at the end of the file.
Since the package is under development it is not a real Mathematica package.
References:
[1] The R Core Team, R Language Definition, (2015).
URL: https://cran.r-project.org/doc/manuals/r-release/R-lang.pdf
[2] D. Bates, M. Maechler, Sparse and Dense Matrix Classes and Methods, Package 'Matrix', (2015).
URL: https://cran.r-project.org/web/packages/Matrix/Matrix.pdf.
This file was created using Mathematica Plugin for IntelliJ IDEA.
Anton Antonov
Windermere, FL, USA
2015-09-27
*)
ClearAll[RSparseMatrix, MakeRSparseMatrix, ToRSparseMatrix, RowNames,
ColumnNames, DimensionNames, RowsCount, ColumnsCount, SetRowNames, SetColumnNames, SetDimensionNames ]
(* Predicate(s) *)
RSparseMatrixQ[x_] := Head[x] === RSparseMatrix;
(*MakeRSparseMatrix[obj_]:=RSparseMatrix[<|"SparseMatrix"->SparseArray[args],"ColumnNames"\[Rule]None,"RowNames"\[Rule]None,"DimensionNames"\[Rule]None|>];*)
(*Creation and conversion*)
RSparseMatrix::rnset =
"The row names `1` are expected to be a list of strings with length that equals the number of rows (`2`) of the RSparseMatrix object.";
RSparseMatrix::cnset =
"The column names `1` are expected to be a list of strings with length that equals the number of columns (`2`) of the RSparseMatrix object.";
RSparseMatrix::dnset =
"The dimension names `1` are expected to be a list of two strings.";
ToRSparseMatrix::arg1 =
"The first argument is expected to be a sparse array, a dataset with two dimensions, or a RSparseMatrix object";
Options[MakeRSparseMatrix] = {"RowNames" -> None, "ColumnNames" -> None, "DimensionNames" -> None};
MakeRSparseMatrix[rules_, opts : OptionsPattern[]] :=
MakeRSparseMatrix[rules, Automatic, 0, opts];
MakeRSparseMatrix[rules_, dims_, val_, opts : OptionsPattern[]] :=
Block[{sarr},
sarr = SparseArray[rules, dims, val];
ToRSparseMatrix[sarr, opts]
];
MakeRSparseMatrix[triplets:_?MatrixQ, opts : OptionsPattern[]] :=
MakeRSparseMatrix[triplets, Automatic, 0, opts]/; Dimensions[triplets][[2]] == 3;
MakeRSparseMatrix[triplets:_?MatrixQ, dims_, val_, opts : OptionsPattern[]] :=
Block[{sarr, rowNames, colNames, rules},
rowNames = Union[ triplets[[All,1]] ];
rowNames = AssociationThread[ rowNames, Range[Length[rowNames]]];
colNames = Union[ triplets[[All,2]] ];
colNames = AssociationThread[ colNames, Range[Length[colNames]]];
rules = triplets;
rules[[All,1]] = rowNames /@ rules[[All,1]];
rules[[All,2]] = colNames /@ rules[[All,2]];
sarr = SparseArray[Most[#]->Last[#]& /@ rules];
ToRSparseMatrix[sarr, "RowNames"-> Map[ToString,Keys[rowNames]], "ColumnNames"-> Map[ToString,Keys[colNames]], opts]
]/; Dimensions[triplets][[2]] == 3;
Options[ToRSparseMatrix] = Options[MakeRSparseMatrix];
ToRSparseMatrix[rmat_RSparseMatrix, opts : OptionsPattern[]] :=
ToRSparseMatrix[rmat[[1]]["SparseMatrix"], opts,
"RowNames" -> RowNames[rmat], "ColumnNames" -> ColumnNames[rmat],
"DimensionNames" -> DimensionNames[rmat]];
ToRSparseMatrix[sarr_SparseArray, opts : OptionsPattern[]] :=
Block[{rnames, cnames, dnames},
rnames = OptionValue[ToRSparseMatrix, "RowNames"];
cnames = OptionValue[ToRSparseMatrix, "ColumnNames"];
dnames = OptionValue[ToRSparseMatrix, "DimensionNames"];
If[! (rnames === None || (MatchQ[rnames, {_String ..}] && Length[rnames] == Dimensions[sarr][[1]])),
Message[RSparseMatrix::rnset, rnames, Dimensions[sarr][[1]]];
Return[$Failed]
];
If[! (cnames === None || (MatchQ[cnames, {_String ..}] && Length[cnames] == Dimensions[sarr][[2]])),
Message[RSparseMatrix::cnset, cnames, Dimensions[sarr][[2]]];
Return[$Failed]
];
If[dnames === {None, None}, dnames = None];
If[ MatchQ[dnames, {_String, None}] || MatchQ[dnames, {None, _String}], dnames = dnames /. None->"" ];
If[! (dnames === None || (MatchQ[dnames, {_String ..}] && Length[dnames] == 2)),
Message[RSparseMatrix::dnset, dnames]; Return[$Failed]
];
RSparseMatrix[<|"SparseMatrix" -> sarr,
"RowNames" ->
If[rnames === None, None,
AssociationThread[rnames, Range[Dimensions[sarr][[1]]]]],
"ColumnNames" ->
If[cnames === None, None,
AssociationThread[cnames, Range[Dimensions[sarr][[2]]]]],
"DimensionNames" ->
If[dnames === None, None, AssociationThread[dnames, {1, 2}]]|>]
];
ToRSparseMatrix[ds_Dataset, opts : OptionsPattern[]] :=
Block[{rows, dsRownames, dsColnames, vals, res},
rows = Normal[ds];
If[AssociationQ[rows],
dsRownames = Keys[rows];
rows = rows /@ dsRownames,
(*ELSE*)
dsRownames = None;
];
If[AssociationQ[rows[[1]]],
dsColnames = Keys[rows[[1]]];
vals = Map[Values, rows],
(*ELSE*)
dsColnames = None;
vals = rows;
];
res = ToRSparseMatrix[SparseArray[vals], "RowNames" -> dsRownames, "ColumnNames" -> dsColnames];
If[ Length[{opts}] == 0, res,
ToRSparseMatrix[ res, opts ]
]
] /; Length[Dimensions[ds]] == 2;
ToRSparseMatrix[xtabs_Association, opts : OptionsPattern[] ] :=
Block[{},
ToRSparseMatrix[ xtabs["SparseMatrix"],
"RowNames" -> Map[ToString, xtabs["RowNames"]],
"ColumnNames" -> Map[ToString, xtabs["ColumnNames"]],
opts
]
]/; KeyExistsQ[xtabs, "SparseMatrix"] && KeyExistsQ[xtabs, "RowNames"] && KeyExistsQ[xtabs, "ColumnNames"];
ToRSparseMatrix[___] := Message[ToRSparseMatrix::arg1];
SparseArray[rmat_RSparseMatrix] ^:= rmat[[1]]["SparseMatrix"];
(* Setters *)
SetAttributes[SetRowNames, HoldFirst]
SetRowNames[ rmat_, names_:{_String..} ] :=
Block[{res},
res = ToRSparseMatrix[rmat,"RowNames"->names,"ColumnNames"->ColumnNames[rmat],"DimensionNames"->DimensionNames[rmat]];
If[ Head[res] === RSparseMatrix,
rmat = res; rmat,
$Failed
]
];
SetAttributes[SetColumnNames, HoldFirst]
SetColumnNames[ rmat_, names_:{_String..} ] :=
Block[{res},
res = ToRSparseMatrix[rmat,"RowNames"->RowNames[rmat],"ColumnNames"->names,"DimensionNames"->DimensionNames[rmat]];
If[ Head[res] === RSparseMatrix,
rmat = res; rmat,
$Failed
]
];
SetAttributes[SetDimensionNames, HoldFirst]
SetDimensionNames[ rmat_, names_:{_String..} ] :=
Block[{res},
res = ToRSparseMatrix[rmat,"RowNames"->RowNames[rmat],"ColumnNames"->ColumnNames[rmat],"DimensionNames"->names];
If[ Head[res] === RSparseMatrix,
rmat = res; rmat,
$Failed
]
];
(*Query methods*)
RowNames[sarr_RSparseMatrix] :=
If[sarr[[1]]["RowNames"] === None, None, Keys[sarr[[1]]["RowNames"]]];
ColumnNames[sarr_RSparseMatrix] :=
If[sarr[[1]]["ColumnNames"] === None, None, Keys[sarr[[1]]["ColumnNames"]]];
DimensionNames[sarr_RSparseMatrix] :=
If[sarr[[1]]["DimensionNames"] === None, {None, None}, Keys[sarr[[1]]["DimensionNames"]]];
ArrayRules[RSparseMatrix[obj_]] ^:=
ArrayRules[RSparseMatrix[obj][[1]]["SparseMatrix"]];
Dimensions[RSparseMatrix[obj_]] ^:=
Dimensions[RSparseMatrix[obj][[1]]["SparseMatrix"]];
RowsCount[r_RSparseMatrix] := Dimensions[r][[1]];
ColumnsCount[r_RSparseMatrix] := Dimensions[r][[2]];
(*Transpose*)
Transpose[RSparseMatrix[obj_]] ^:=
Block[{assoc = obj},
assoc["SparseMatrix"] = Transpose[obj["SparseMatrix"]];
assoc["ColumnNames"] = obj["RowNames"];
assoc["RowNames"] = obj["ColumnNames"];
assoc["DimensionNames"] = If[obj["DimensionNames"] === None, None, Reverse[obj["DimensionNames"]]];
RSparseMatrix[assoc]
];
(*Showing the matrix*)
MatrixForm[RSparseMatrix[obj_], args___] ^:=
MatrixForm[RSparseMatrix[obj][[1]]["SparseMatrix"], args,
TableHeadings -> {RowNames[RSparseMatrix[obj]],
ColumnNames[RSparseMatrix[obj]]}];
MatrixPlot[RSparseMatrix[obj_], args___] ^:=
MatrixPlot[RSparseMatrix[obj][[1]]["SparseMatrix"], args];
(*Sums*)
RowSums[rmat_RSparseMatrix] := Total[rmat[[1]]["SparseMatrix"], {2}];
ColumnSums[rmat_RSparseMatrix] := Total[rmat[[1]]["SparseMatrix"]];
Total[rmat_RSparseMatrix, args___] ^:= Total[rmat[[1]]["SparseMatrix"], args];
(*Dot product*)
(*Note that here we do not have to define the behavior for Dot[r1,r2,r3,r4,\[Ellipsis]] .*)
Dot[RSparseMatrix[obj1_], RSparseMatrix[obj2_]] ^:=
Block[{res},
res = Dot[RSparseMatrix[obj1][[1]]["SparseMatrix"], RSparseMatrix[obj2][[1]]["SparseMatrix"]];
ToRSparseMatrix[res, "RowNames" -> RowNames[RSparseMatrix[obj1]],
"ColumnNames" -> ColumnNames[RSparseMatrix[obj2]],
"DimensionNames" -> {DimensionNames[RSparseMatrix[obj1]][[1]],
DimensionNames[RSparseMatrix[obj2]][[2]]}]
];
Dot[RSparseMatrix[obj_], x_] ^:=
Block[{res},
res = Dot[RSparseMatrix[obj][[1]]["SparseMatrix"], x];
ToRSparseMatrix[res, "RowNames" -> RowNames[RSparseMatrix[obj]],
"DimensionNames" -> {DimensionNames[RSparseMatrix[obj]][[1]], ""}]
];
Dot[x_, RSparseMatrix[obj_]] ^:=
Block[{res},
res = Dot[x, RSparseMatrix[obj][[1]]["SparseMatrix"]];
ToRSparseMatrix[res, "ColumnNames" -> ColumnNames[RSparseMatrix[obj]],
"DimensionNames" -> {"", DimensionNames[RSparseMatrix[obj]][[2]]}]
];
(* Arithmetic operators *)
(*Here we need to have an option to respect or to ignore the row names and column names.*)
Times[rmat1_RSparseMatrix, rmat2_RSparseMatrix] ^:=
Block[{},
If[ TrueQ[ RowNames[rmat1] == RowNames[rmat2] && ColumnNames[rmat1] == ColumnNames[rmat2] ],
ToRSparseMatrix[Times[SparseArray[rmat1], SparseArray[rmat2]],
"RowNames" -> RowNames[rmat1], "ColumnNames" -> ColumnNames[rmat1],
"DimensionNames" -> DimensionNames[rmat1]],
(*ELSE*)
ToRSparseMatrix[Times[SparseArray[rmat1], SparseArray[rmat2]]]
]
];
Times[rmat1_RSparseMatrix, x_] ^:=
ToRSparseMatrix[Times[SparseArray[rmat1], x], "RowNames" -> RowNames[rmat1],
"ColumnNames" -> ColumnNames[rmat1],
"DimensionNames" -> DimensionNames[rmat1]];
Times[x_, rmat1_RSparseMatrix] ^:=
ToRSparseMatrix[Times[x, SparseArray[rmat1]], "RowNames" -> RowNames[rmat1],
"ColumnNames" -> ColumnNames[rmat1],
"DimensionNames" -> DimensionNames[rmat1]];
(* Same as above for Plus. *)
Plus[rmat1_RSparseMatrix, rmat2_RSparseMatrix] ^:=
Block[{},
If[TrueQ[ RowNames[rmat1] == RowNames[rmat2] && ColumnNames[rmat1] == ColumnNames[rmat2] ],
ToRSparseMatrix[Plus[SparseArray[rmat1], SparseArray[rmat2]],
"RowNames" -> RowNames[rmat1], "ColumnNames" -> ColumnNames[rmat1],
"DimensionNames" -> DimensionNames[rmat1]],
(*ELSE*)
ToRSparseMatrix[Plus[SparseArray[rmat1], SparseArray[rmat2]]]
]
];
Plus[rmat1_RSparseMatrix, x_] ^:=
ToRSparseMatrix[Plus[SparseArray[rmat1], x], "RowNames" -> RowNames[rmat1],
"ColumnNames" -> ColumnNames[rmat1],
"DimensionNames" -> DimensionNames[rmat1]];
Plus[x_, rmat1_RSparseMatrix] ^:=
ToRSparseMatrix[Plus[x, SparseArray[rmat1]], "RowNames" -> RowNames[rmat1],
"ColumnNames" -> ColumnNames[rmat1],
"DimensionNames" -> DimensionNames[rmat1]];
(* Part *)
Part[RSparseMatrix[obj_], s1 : (_String | {_String ..})] ^:=
Block[{ i1 },
i1 = If[ ListQ[s1], obj["RowNames"] /@ s1, obj["RowNames"] @ s1 ];
Part[ RSparseMatrix[obj], i1, All ]
];
Part[RSparseMatrix[obj_], s1 : (_String | {_String ..}), s2 : (_String | {_String ..})] ^:=
Block[{ i1, i2 },
i1 = If[ ListQ[s1], obj["RowNames"] /@ s1, obj["RowNames"] @ s1 ];
i2 = If[ ListQ[s2], obj["ColumnNames"] /@ s2, obj["ColumnNames"] @ s2 ];
Part[ RSparseMatrix[obj], i1, i2 ]
];
Part[RSparseMatrix[obj_], s1 : (_String | {_String ..}), s2_] ^:=
Block[{ i1 },
i1 = If[ ListQ[s1], obj["RowNames"] /@ s1, obj["RowNames"] @ s1 ];
Part[ RSparseMatrix[obj], i1, s2 ]
];
Part[RSparseMatrix[obj_], s1_, s2 : (_String | {_String ..})] ^:=
Block[{ i2 },
i2 = If[ ListQ[s2], obj["ColumnNames"] /@ s2, obj["ColumnNames"] @ s2 ];
Part[ RSparseMatrix[obj], s1, i2 ]
];
Part[RSparseMatrix[obj_], s1_, s2_] ^:=
Block[{smat},
smat = Part[ obj["SparseMatrix"], s1, s2 ];
If[Head[smat] === Part,
smat,
If[ MatrixQ[smat],
ToRSparseMatrix[smat,
"RowNames" -> If[ RowNames[RSparseMatrix[obj]]===None, None, RowNames[RSparseMatrix[obj]][[s1]] ],
"ColumnNames" -> If[ ColumnNames[RSparseMatrix[obj]]===None, None, ColumnNames[RSparseMatrix[obj]][[s2]] ],
"DimensionNames" -> DimensionNames[RSparseMatrix[obj]]],
(* ELSE *)
smat
]
]
];
(* RowBind, ColumnBind *)
(* Here we need to have an option to respect or to ignore the row names and column names for RowBind and ColumnBind respectively.
RowBind[r1_RSparseMatrix, r2_RSparseMatrix, opts : OptionsPattern[]]
ColumnBind[r1_RSparseMatrix, r2_RSparseMatrix, opts : OptionsPattern[]]
There are three solutions (1) using array rules, (2) using matrix padding, ArrayPad, ArrayReshape, PadLeft and PadRight, and (3) using Join.
Here are the steps of the first algorithm for RowBind:
1. Get array rules of both sparse arrays.
2. Increment the row indices of the second one with the number of rows of the first one.
3. Join the rules and make a new SparseArray object.
4. Make a new RSparseMatrix object with its row names being the joined row names of the arguments.
Here are the steps of the second algorithm for RowBind:
1. Pad from below the sparse array of the first argument to the number of result rows.
2. Pad from above the sparse array of the second argument to the number of result rows.
3. Sum the padded sparse arrays.
4. Make the result RSparseMatrix object with the row names being the joined row names of the arguments.
Using Join is of course straightforward.
Since Association removes duplication of keys special care has to be taken when joining the row and column names.
*)
(*Options[RowBind] = {"IgnoreColumnNames" -> False};*)
RowBind[r1_RSparseMatrix, r2_RSparseMatrix, rm__] := RowBind[ RowBind[r1, r2], rm];
RowBind[rm:{_RSparseMatrix..}] := Fold[RowBind, First[rm], Rest[rm]];
RowBind[r1_RSparseMatrix, r2_RSparseMatrix ] :=
Block[{sarr, joinedRowAssoc, resRowNames},
sarr = Join[ SparseArray[r1], SparseArray[r2] ];
(* Special handling of duplication of row names in the result. *)
joinedRowAssoc = Join[r1[[1]]["RowNames"], r2[[1]]["RowNames"]];
If[Length[joinedRowAssoc] == Dimensions[sarr][[1]],
resRowNames = Join[RowNames[r1], RowNames[r2]],
resRowNames =
Join[# <> ".1" & /@ RowNames[r1], # <> ".2" & /@ RowNames[r2]]
];
ToRSparseMatrix[sarr, "RowNames" -> resRowNames,
"ColumnNames" -> ColumnNames[r1], "DimensionNames" -> DimensionNames[r1]]
];
(*Options[ColumnBind] = {"IgnoreRowNames" -> False};*)
ColumnBind[r1_RSparseMatrix, r2_RSparseMatrix, rm__] := ColumnBind[ ColumnBind[r1, r2], rm];
ColumnBind[rm:{_RSparseMatrix..}] := Fold[ColumnBind, First[rm], Rest[rm]];
ColumnBind[r1_RSparseMatrix, r2_RSparseMatrix ] :=
Block[{sarr, joinedRowAssoc, resColumnNames},
sarr = Transpose@
Join[Transpose@SparseArray[r1], Transpose@SparseArray[r2]];
(* Special handling of duplication of column names in the result. *)
joinedRowAssoc = Join[r1[[1]]["ColumnNames"], r2[[1]]["ColumnNames"]];
If[Length[joinedRowAssoc] == Dimensions[sarr][[2]],
resColumnNames = Join[ColumnNames[r1], ColumnNames[r2]],
resColumnNames =
Join[# <> ".1" & /@ ColumnNames[r1], # <> ".2" & /@ ColumnNames[r2]]
];
ToRSparseMatrix[sarr, "RowNames" -> RowNames[r1],
"ColumnNames" -> resColumnNames, "DimensionNames" -> DimensionNames[r1]]
];
Clear[ImposeRowNames, ImposeColumnNames]
ImposeRowNames[rmat_RSparseMatrix, rowNames : {_String ..}] :=
Block[{rmRowNames = RowNames[rmat], resMat, pos},
resMat =
Table[SparseArray[{0}, ColumnsCount[rmat]], {Length[rowNames]}];
resMat =
Fold[Function[{m, rn},
pos = Position[rowNames, RowNames[rmat][[rn]]];
If[Length[pos] == 0, m,
pos = pos[[1, 1]];
ReplacePart[m, pos -> rmat[[rn, All]]]
]
], resMat, Range[RowsCount[rmat]]];
ToRSparseMatrix[SparseArray[resMat], "RowNames" -> rowNames, "ColumnNames" -> ColumnNames[rmat]]
];
ImposeColumnNames[rmat_RSparseMatrix, colNames : {_String ..}] :=
Transpose[ImposeRowNames[Transpose[rmat], colNames]];
Clear[RSparseMatrixToTriplets]
RSparseMatrixToTriplets[ rsmat_RSparseMatrix ] :=
Block[{t},
t = Most[ArrayRules[rsmat]];
t[[All, 1, 1]] = t[[All, 1, 1]] /. Dispatch[Thread[Range[RowsCount[rsmat]] -> RowNames[rsmat]]];
t[[All, 1, 2]] = t[[All, 1, 2]] /. Dispatch[Thread[Range[ColumnsCount[rsmat]] -> ColumnNames[rsmat]]];
Flatten/@ (List @@@ t)
];
(* Delegation to SparseArray functions *)
(* This is similar to the OOP design pattern Decorator.
The implementation is still experimental.
New functions for RSparseMatrix objects have to be added into the do-not-decorate list.
Note that this decoration is very aggressive and it might have un-forseen effects.
*)
(* Format *)
Format[RSparseMatrix[obj_]] := obj["SparseMatrix"];
(*F_[rmat_RSparseMatrix, args___] ^:=*)
(*Block[{res = F[SparseArray[rmat], args]},*)
(*Print["RSparseMatrix decoration::F=",F];*)
(*Print["RSparseMatrix decoration::res=",res];*)
(*If[MatrixQ[res],*)
(*RSparseMatrix[*)
(*Join[<|"SparseMatrix" -> SparseArray[res]|>, Rest[rmat[[1]]]]],*)
(*res*)
(*]*)
(*] /;*)
(*! MemberQ[*)
(*Join[{"SparseMatrix", "ToRSparseMatrix",*)
(*"RowNames", "ColumnNames", "DimensionNames",*)
(*"SetRowNames", "SetColumnNames", "SetDimensionNames",*)
(*"MatrixForm", "MatrixPlot",*)
(*"Dimensions", "ArrayRules",*)
(*"Total", "RowSums", "ColumnSums", "RowsCount", "ColumnsCount",*)
(*"Dot", "Plus", "Times", "Part",*)
(*"RowBind", "ColumnBind",*)
(*"Head", "Format", "Print"},*)
(*Names["System`*Hold*"],*)
(*Names["System`Inactiv*"],*)
(*Names["System`Activ*"]*)
(*], SymbolName[F] ];*)