{"record":{"id":"6bd1189bb6275ff8","repo":"stanfordnlp/CoreNLP","slug":"non-preterminal-nodes-of-size-1-should-have-alread","errorCode":null,"errorMessage":"Non-preterminal nodes of size 1 should have already been collapsed","messagePattern":"Non-preterminal nodes of size 1 should have already been collapsed","errorType":"exception","errorClass":"ForwardPropagationException","httpStatus":null,"severity":"error","filePath":"src/edu/stanford/nlp/sentiment/SentimentCostAndGradient.java","lineNumber":509,"sourceCode":"   * useful annotation except when training.\n   */\n  public void forwardPropagateTree(Tree tree) {\n    SimpleMatrix nodeVector; // initialized below or Exception thrown // = null;\n    SimpleMatrix classification; // initialized below or Exception thrown // = null;\n\n    if (tree.isLeaf()) {\n      // We do nothing for the leaves.  The preterminals will\n      // calculate the classification for this word/tag.  In fact, the\n      // recursion should not have gotten here (unless there are\n      // degenerate trees of just one leaf)\n      throw new ForwardPropagationException(\"We should not have reached leaves in forwardPropagate\");\n    } else if (tree.isPreTerminal()) {\n      classification = model.getUnaryClassification(tree.label().value());\n      String word = tree.children()[0].label().value();\n      SimpleMatrix wordVector = model.getWordVector(word);\n      nodeVector = NeuralUtils.elementwiseApplyTanh(wordVector);\n    } else if (tree.children().length == 1) {\n      throw new ForwardPropagationException(\"Non-preterminal nodes of size 1 should have already been collapsed\");\n    } else if (tree.children().length == 2) {\n      forwardPropagateTree(tree.children()[0]);\n      forwardPropagateTree(tree.children()[1]);\n\n      String leftCategory = tree.children()[0].label().value();\n      String rightCategory = tree.children()[1].label().value();\n      SimpleMatrix W = model.getBinaryTransform(leftCategory, rightCategory);\n      classification = model.getBinaryClassification(leftCategory, rightCategory);\n\n      SimpleMatrix leftVector = RNNCoreAnnotations.getNodeVector(tree.children()[0]);\n      SimpleMatrix rightVector = RNNCoreAnnotations.getNodeVector(tree.children()[1]);\n      SimpleMatrix childrenVector = NeuralUtils.concatenateWithBias(leftVector, rightVector);\n      if (model.op.useTensors) {\n        SimpleTensor tensor = model.getBinaryTensor(leftCategory, rightCategory);\n        SimpleMatrix tensorIn = NeuralUtils.concatenate(leftVector, rightVector);\n        SimpleMatrix tensorOut = tensor.bilinearProducts(tensorIn);\n        nodeVector = NeuralUtils.elementwiseApplyTanh(W.mult(childrenVector).plus(tensorOut));\n      } else {","sourceCodeStart":491,"sourceCodeEnd":527,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/sentiment/SentimentCostAndGradient.java#L491-L527","documentation":"The RNN model assumes binary-branching trees where any non-preterminal internal node has exactly two children. A non-preterminal node with a single child indicates the tree was not properly binarized/collapsed, so ForwardPropagationException is thrown.","triggerScenarios":"Passing non-binarized constituency trees (unary productions like (NP (NN dog))) directly to training/evaluation without collapsing unary nodes.","commonSituations":"Using parser output without binarization, custom treebank preprocessing that skipped the collapse unary step, mixing trees from different preprocessing pipelines.","solutions":["Binarize and collapse unary (size-1) non-preterminal nodes before training (use the tooling in the sentiment package, e.g. CollinsHeadFinder-based binarization used in dataset conversion)","Verify with a tree traversal that every non-leaf, non-preterminal node has 2 children","Reconvert your dataset with ReadSentimentDataset, which performs the collapsing"],"exampleFix":"// before\n(NP (NN dog))\n// after (collapsed/binarized)\n(NP* (NN dog))","handlingStrategy":"validation","validationCode":"boolean isBinarized(Tree t) {\n  if (t.isLeaf() || t.isPreTerminal()) return true;\n  return t.children().length == 2 && Arrays.stream(t.children()).allMatch(this::isBinarized);\n}\nif (!isBinarized(root)) throw new IllegalStateException(\"Tree not binarized\");","typeGuard":null,"tryCatchPattern":"try { forwardPropagate(tree); } catch (ForwardPropagationException e) { if (e.getMessage().contains(\"size 1\")) { collapseUnaries(tree); } else throw e; }","preventionTips":["Collapse unary non-preterminal nodes before training","Validate binarization of every tree in the dataset","Use the same preprocessing pipeline for train and eval data"],"tags":["neural-network","tree-structure"],"backgroundTag":"internal-invariant-violation","analyzedSha":"1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a","analyzedAt":"2026-09-10T02:24:07.274Z","contentChangedAt":"2026-09-10T02:24:07.274Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}