-
Notifications
You must be signed in to change notification settings - Fork 135
Make ShapedArray.description's maxScalarCountPerLine user-customizable #1168
Comments
Here's the PR that added The original goal with |
Thanks @dan-zheng. I copied the above linked swift-apis code into my project: extension String {
/// Returns a string of the specified length, padded with whitespace to the left.
func leftPadded(toLength length: Int) -> String {
return repeatElement(" ", count: max(0, length - count)) + self
}
}
public extension ShapedArray {
func vectorDescription(
indentLevel: Int,
edgeElementCount: Int,
maxScalarLength: Int,
maxScalarCountPerLine: Int,
summarizing: Bool
) -> String {
// Get scalar descriptions.
func scalarDescription(_ element: Element) -> String {
let description = String(describing: element)
return description.leftPadded(toLength: maxScalarLength)
}
var scalarDescriptions: [String] = []
if summarizing && count > 2 * edgeElementCount {
scalarDescriptions += prefix(edgeElementCount).map(scalarDescription)
scalarDescriptions += ["..."]
scalarDescriptions += suffix(edgeElementCount).map(scalarDescription)
} else {
scalarDescriptions += map(scalarDescription)
}
// Combine scalar descriptions into lines, based on the scalar count per line.
let lines = stride(
from: scalarDescriptions.startIndex,
to: scalarDescriptions.endIndex,
by: maxScalarCountPerLine
).map { i -> ArraySlice<String> in
let upperBound = Swift.min(
i.advanced(by: maxScalarCountPerLine),
scalarDescriptions.count)
return scalarDescriptions[i..<upperBound]
}
// Return lines joined with separators.
let lineSeparator = ",\n" + String(repeating: " ", count: indentLevel + 1)
return lines.enumerated().reduce(into: "[") { result, entry in
let (i, line) = entry
result += line.joined(separator: ", ")
result += i != lines.count - 1 ? lineSeparator : ""
} + "]"
}
func description(
indentLevel: Int,
edgeElementCount: Int,
maxScalarLength: Int,
maxScalarCountPerLine: Int,
summarizing: Bool
) -> String {
// Handle scalars.
if let scalar = scalar {
return String(describing: scalar)
}
// Handle vectors, which have special line-width-sensitive logic.
if rank == 1 {
return vectorDescription(
indentLevel: indentLevel,
edgeElementCount: edgeElementCount,
maxScalarLength: maxScalarLength,
maxScalarCountPerLine: maxScalarCountPerLine,
summarizing: summarizing)
}
// Handle higher-rank tensors.
func elementDescription(_ element: Element) -> String {
return element.description//(
/*indentLevel: indentLevel + 1,*/
/*edgeElementCount: edgeElementCount,*/
/*maxScalarLength: maxScalarLength,*/
/*maxScalarCountPerLine: maxScalarCountPerLine,*/
/*summarizing: summarizing)*/
}
var elementDescriptions: [String] = []
if summarizing && count > 2 * edgeElementCount {
elementDescriptions += prefix(edgeElementCount).map(elementDescription)
elementDescriptions += ["..."]
elementDescriptions += suffix(edgeElementCount).map(elementDescription)
} else {
elementDescriptions += map(elementDescription)
}
// Return lines joined with separators.
let lineSeparator =
"," + String(repeating: "\n", count: rank - 1)
+ String(repeating: " ", count: indentLevel + 1)
return elementDescriptions.enumerated().reduce(into: "[") { result, entry in
let (i, elementDescription) = entry
result += elementDescription
result += i != elementDescriptions.count - 1 ? lineSeparator : ""
} + "]"
}
} And this achieved what I wanted:
The corresponding values are all lined up visually now. Given that it works as-is, I was wondering why it needed to be fileprivate. I'm not seeing any options in Pytorch's set_printoptions that would achieve this. Nothing stands out in the numpy implementation that would achieve it either. No worries if this is outside the scope of the intended API. |
Nice, thanks for sharing your code snippet! Could you please complete the example by showing the invocation of I would describe your change as "adding Supporting this change tentatively sounds good to me (I haven't thought about it super hard). Do you have some intuition why |
Each print(myTensor[TensorRange.ellipsis, variableIndex].array.description(indentLevel: 0,
edgeElementCount: 50,
maxScalarLength: 10,
maxScalarCountPerLine: 8,
summarizing: true)) Oh yes, the idea of opening currently private API is incidental. The core idea is supporting
public func description(
lineWidth: Int = 80,
edgeElementCount: Int = 3,
summarizing: Bool = false
) -> String {
let maxScalarLength = scalars.lazy.map { String(describing: $0).count }.max() ?? 3
let maxScalarCountPerLine = Swift.max(1, lineWidth / maxScalarLength) where only the This was born out of a desire to understand my loss values. Printing a snippet of my model's output alongside input and target allows me to understand how close the outputs really are to the targets in absolute value for a given valLoss. |
Thanks for the context! Feel free to open a PR, if you'd like to upstream your |
Thanks @dan-zheng, I'll plan to open a pull request with some unit tests after Christmas. |
Thank you! Take your time - Santa and we are patiently waiting for you after Xmas. |
🎅 |
tbh GitHub needs to enable more than eight reaction emoji 🙎🏻♂️ |
Here is
ShapedArray
'sfileprivate func description( indentLevel: Int, edgeElementCount: Int, maxScalarLength: Int, maxScalarCountPerLine: Int, summarizing: Bool ) -> String
.Is there any reason this is marked
fileprivate
? It's currently accessible only via thepublic func description( lineWidth: Int = 80, edgeElementCount: Int = 3, summarizing: Bool = false )
where themaxScalarCountPerLine
is calculated for me:Calculating the
maxScalarCountPerLine
independently for each Tensor leads to this problem:Each Tensor has a different number of scalars per line, so the values are visually shifted in each of the three descriptions. This makes it difficult to visually inspect the values at the same position in each tensor.
I would like to be able to force the max number of scalars per line for each
Tensor
so that the values are more readily visually comparable.The text was updated successfully, but these errors were encountered: