{"id":19755703,"url":"https://github.com/hrbigelow/opschema","last_synced_at":"2026-05-15T19:31:34.442Z","repository":{"id":60330934,"uuid":"524852384","full_name":"hrbigelow/opschema","owner":"hrbigelow","description":"Semantic Indices for Tensor Operations","archived":false,"fork":false,"pushed_at":"2023-02-19T05:40:24.000Z","size":2358,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-25T11:56:55.109Z","etag":null,"topics":["machine-learning","tensorflow"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hrbigelow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-15T04:14:47.000Z","updated_at":"2023-01-31T20:12:21.000Z","dependencies_parsed_at":"2025-02-28T02:11:21.756Z","dependency_job_id":"ff03bf28-9066-4b07-8c9e-8dc5ae71f802","html_url":"https://github.com/hrbigelow/opschema","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/hrbigelow/opschema","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrbigelow%2Fopschema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrbigelow%2Fopschema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrbigelow%2Fopschema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrbigelow%2Fopschema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hrbigelow","download_url":"https://codeload.github.com/hrbigelow/opschema/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrbigelow%2Fopschema/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33076158,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T11:35:32.926Z","status":"ssl_error","status_checked_at":"2026-05-15T11:35:31.362Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["machine-learning","tensorflow"],"created_at":"2024-11-12T03:12:50.760Z","updated_at":"2026-05-15T19:31:34.425Z","avatar_url":"https://github.com/hrbigelow.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# opschema \n\nA system to build input constraint schemas for TensorFlow operations\n\nInstall from PyPI:\n\n    pip install opschema\n    pip install git+https://github.com/hrbigelow/opschema\n\n# Motivation\n\nTensorFlow Python is a workhorse of the Machine Learning world used by many\nthousands of developers.  Tensor ops are often (necessarily) highly polymorphic with\nintricate shape and other required relationships in inputs.  If these are not\nmet, often the exception will arise from several stack levels down the\ncodebase.  Because of this, it is frequently not clear to the user what input\nconstraints are violated and what should be done to correct the error.\n\nUser demand for more functionality is ever present, and so TensorFlow evolves\nat a quick pace, presenting a challenge for developers to keep documentation\nupdated, as well as the many messages in exceptions to describe errors.\n\nDocumentation very often does not fully describe the legal inputs to ops. Finding\nout whether a particular call is legal must be done by trial and error in many\ncases.\n\n# Introduction\n\nopschema provides an API for building *op schemas* for representing TensorFlow\noperations.  Once written, a schema represents a single operation, such as\n`tf.nn.convoution` or `tf.nn.bias_add`, etc.  The schema defines what inputs are\nlegal for the op.  Once defined, it provides four functionalities:\n\n* provide programmatically generated error messages using actual parameter\n  names and identifiers registered in the schema as a single source of truth.\n\n* provide programmatically generated documentation of legal call\n  configurations, using actual parameter names, and the same identifiers used\n  in error messages.\n\n* generate a complete set of legal (and a particular set of illegal) inputs for\n  the op, useful for comprehensive ops unit testing\n\n* empirically validate schema correctness against TensorFlow op, given in TP,\n  TN, FP and FN counts\n\n# Proof of Concept repo only\n\nopschema is intended only as a proof-of-concept, a demonstration of an idea\nthat might be integrated into TensorFlow.  In order to be viable outside of\nTensorFlow, it would need to provide a set of schemas for every version of\nTensorFlow, as ops were augmented and updated, which is unmaintainable.\n\nInstead, the viable path would be to have an internal schema API integrated\ninto the the TensorFlow codebase, with each user-level op having a schema\ndefinition which co-evolves as the op itself is augmented over the different\nversions of TensorFlow.\n\n# Synopsis\n\nList available op schemas (defined under opschema/ops)\n\n    python -m opschema.cl list\n\nProgrammatically generate documentation for an op\n\n    python -m opschema.cl explain OP_PATH [-i|--include_inventory]\n\nPrint the graphs associated with an op in .pdf format (requires graphviz)\n\n    python -m opschema.cl graph OP_PATH OUT_DIR\n\nValidate an op schema against the TensorFlow op it represents  \n\n    python -m opschema.cl validate OP_PATH OUT_DIR \\\n        [--test_ids] \\\n        [--skip_ids] \\\n        [--max_dtype_err=0] \\\n        [--rand_seed=0] \\\n        [--show_traceback]\n\n# Overview \n\n`opschema` provides an API for writing *schemas* for TensorFlow ops.  A schema\nhere means a set of rules that define what combinations of inputs are legal.\nOnce a schema is defined, you can use opschema to generate a complete set of\ntest inputs for the op for all legal combinations of tensor dtypes, shapes, and\ncombinations of other control arguments such as `data_format` etc.  In\naddition, a subset of illegal inputs can be generated as well, which are useful\nfor comparing TensorFlow's exception with opschema's error message.\n\n# Example Error Messages\n\nThis section gives many examples of calls to TensorFlow ops which raise an\nexception.  Each example compares the TensorFlow exception text to the error\nmessage provided by `opschema`.  All examples are generated using the command:\n\n    python -m opschema.cl validate OP_PATH OUT_DIR --max_dtype_err=1\n\nThis command internally uses the schema graphs (described below) to generate\nall possible combinations of valid (and many invalid) inputs for the op, and\nthen run a wrapped version of the op, collecting both `opschema` error message\nand the TensorFlow exception text if any.\n\nopschema provides error messages as a list of one or more 'fixes'.  Each fix\nhas an associated `FixKind`, which is a bitmask of the following enum:\n\n```python\nclass FixKind(enum.Enum):\n    DTypeEquate = 1    # input tensors have differing dtypes but should match\n    DTypeIndiv = 2     # one input tensor has a disallowed dtype\n    ComboExcluded = 4  # this combo of dtypes, index ranks and/or layout is excluded \n    InsertDim = 8      # fix by inserting dimension(s) to a particular tensor \n    DeleteDim = 16     # fix by deleting dimension(s) from a particular tensor\n    IndexUsage = 32    # an index appearing multiple places has differing dimension\n    IndexPred = 64     # an index predicate is violated\n    Layout = 128       # fix by trying an alternate layout\n```\n\nEach example is given in the format:\n\n```\n## ID   CLASS    OP_PATH: ARGS_LIST\nTensorFlow Exception\nTENSORFLOW_EXCEPTION_TEXT\n\nFixKind\nOPSCHEMA_FIX\n\nFixKind\nOPSCHEMA_FIX\n...\n```\n\nCLASS has the following meaning: \n\n    CLASS     TensorFlow     opschema\n    TP        raises         issues error\n    TN        succeeds       none \n    FP        succeeds       issues error\n    FN        raises         none \n\nNote that CLASS does not say anything about how well the TensorFlow exception\nand opschema error message agree.  The goal is for the opschema message to be\nmore informative and lead to a successful correction.  But, the schema\ndefinition is a reverse-engineering process based on the observed behavior of\nthe TensorFlow op.\n\nI have cut-and-pasted a selection of them from multiple ops together by\n`FixKind` for illustration purposes.\n\nIn many of the messages, there will be tables using semantic, one-letter-codes\nassigned to dimensions of tensor shapes.  These codes and tables are explained\nin the [Schema](#schema) section below.  \n\n## DTypeEquate examples\n\nThese errors occur when two input tensors' dtypes differ but should not.  In\nthis situation, TensorFlow assumes that the first dtype was the correct one and\nsuggests changing the second one.   opschema does not make this assumption.\nAdditionally, TensorFlow does not use actual parameter names in its error\nmessages, while opschema always does.\n\nThis constraint is declared using API function [equate_dtypes](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1318).\n\n```\n## 37   TP      tf.nn.conv_transpose: input=[61, 27, 91]:bfloat16, filters=[92, 2, 27]:float64, output_shape=[61, 270, 182], strides=1, padding=VALID, data_format=NCW, dilations=1\nTensorFlow Exception\ncannot compute Conv2DBackpropInput as input #2(zero-based) was expected to be a double tensor but is a bfloat16 tensor [Op:Conv2DBackpropInput]\n\nDTypeEquate\nReceived filters.dtype = float64 and input.dtype = bfloat16.  dtypes of filters and input must match.\n\n## 32   TP      tf.nn.convolution: input=[44, 52, 16]:float16, filters=[79, 26, 21]:qint16, strides=1, padding=VALID, data_format=NCW, dilations=1\ncannot compute Conv2D as input #1(zero-based) was expected to be a half tensor but is a qint16 tensor [Op:Conv2D]\n\nDTypeEquate\nReceived filters.dtype = qint16 and input.dtype = float16.  dtypes of filters and input must match.\n\n## 13   TP      tf.nn.bias_add: value=[71, 8]:int32, bias=[8]:float32, data_format=NC..\nTensorFlow Exception\ncannot compute BiasAdd as input #1(zero-based) was expected to be a int32 tensor but is a float tensor [Op:BiasAdd]\n\nDTypeEquate\nReceived bias.dtype = float32 and value.dtype = int32.  dtypes of bias and value must match.\n```\n\n## DTypeIndiv examples\n\nDTypeIndiv fixes occur when a particular input tensor is declared with\n[valid_dtypes](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1282), for example:\n\n```python\n# from opschema/ops/tf/nn/convolution.py\n# input dtype may be int32, any kind of float, or bfloat16\nop.valid_dtypes('input', ('int32', 'float', 'bfloat16'))\n...\n```\n\nThese errors tend to produce long TensorFlow exceptions that don't mention\nwhich tensor had a disallowed dtype.  opschema mentions the tensor by name and\nwhich dtypes are allowed.\n\n```\n## 55   TP      tf.nn.bias_add: value=[13, 77]:qint8, bias=[77]:qint8, data_format=NC..\nTensorFlow Exception\nCould not find device for node: {{node BiasAdd}} = BiasAdd[T=DT_QINT8, data_format=\"NCHW\"]\nAll kernels registered for op BiasAdd:\n  device='CPU'; T in [DT_COMPLEX128]\n  device='CPU'; T in [DT_COMPLEX64]\n  device='CPU'; T in [DT_DOUBLE]\n  device='CPU'; T in [DT_FLOAT]\n  device='CPU'; T in [DT_BFLOAT16]\n  device='CPU'; T in [DT_HALF]\n  device='CPU'; T in [DT_INT32]\n  device='CPU'; T in [DT_INT8]\n  device='CPU'; T in [DT_UINT8]\n  device='CPU'; T in [DT_INT16]\n  device='CPU'; T in [DT_UINT16]\n  device='CPU'; T in [DT_UINT32]\n  device='CPU'; T in [DT_INT64]\n  device='CPU'; T in [DT_UINT64]\n  device='GPU'; T in [DT_INT32]\n  device='GPU'; T in [DT_DOUBLE]\n  device='GPU'; T in [DT_FLOAT]\n  device='GPU'; T in [DT_HALF]\n [Op:BiasAdd]\n\nDTypeIndiv\nReceived value.dtype = qint8.  Valid dtypes for value are: int8, int16, int32, int64, uint8, uint16, uint32, uint64, float16, float32, float64, bfloat16, complex64 and complex128\n\n## 41   TP      tf.nn.avg_pool: input=[31, 9, 1]:int16, ksize=[1], strides=[43], padding=VALID, data_format=NCW\nValue for attr 'T' of int16 is not in the list of allowed values: half, bfloat16, float, double\n        ; NodeDef: {{node AvgPool}}; Op\u003cname=AvgPool; signature=value:T -\u003e output:T; attr=ksize:list(int),min=4; attr=strides:list(int),min=4; attr=padding:string,allowed=[\"SAME\", \"VALID\"]; attr=data_for\nmat:string,default=\"NHWC\",allowed=[\"NHWC\", \"NCHW\"]; attr=T:type,allowed=[DT_HALF, DT_BFLOAT16, DT_FLOAT, DT_DOUBLE]\u003e [Op:AvgPool]\n\nDTypeIndiv\nReceived input.dtype = int16.  Valid dtypes for input are: bfloat16, float16, float32 and float64\n\n## 129  TP      tf.nn.space_to_depth: input=[17, 46, 2, 8]:qint16, block_size=45, data_format=NHWC\nTensorFlow Exception\nCould not find device for node: {{node SpaceToDepth}} = SpaceToDepth[T=DT_QINT16, block_size=45, data_format=\"NHWC\"]\nAll kernels registered for op SpaceToDepth:\n  device='XLA_CPU_JIT'; T in [DT_FLOAT, DT_DOUBLE, DT_INT32, DT_UINT8, DT_INT16, 16005131165644881776, DT_UINT16, DT_COMPLEX128, DT_HALF, DT_UINT32, DT_UINT64]\n  device='XLA_GPU_JIT'; T in [DT_FLOAT, DT_DOUBLE, DT_INT32, DT_UINT8, DT_INT16, 16005131165644881776, DT_UINT16, DT_COMPLEX128, DT_HALF, DT_UINT32, DT_UINT64]\n  device='GPU'; T in [DT_UINT8]\n  device='GPU'; T in [DT_QINT8]\n  device='GPU'; T in [DT_HALF]\n  device='GPU'; T in [DT_FLOAT]\n  device='CPU'; T in [DT_QINT8]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_VARIANT]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_RESOURCE]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_STRING]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_BOOL]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_COMPLEX128]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_COMPLEX64]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_DOUBLE]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_FLOAT]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_BFLOAT16]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_HALF]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_INT32]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_INT8]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_UINT8]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_INT16]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_UINT16]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_UINT32]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_INT64]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_UINT64]; data_format in [\"NHWC\"]\n [Op:SpaceToDepth]\n\nDTypeIndiv\nReceived input.dtype = qint16.  Valid dtypes for input are: bool, complex64, complex128, qint8, bfloat16, int8, int16, int32, int64, float16, float32, float64, uint8, uint16, uint32 and uint64\n```\n\n## ComboExcluded examples\n\nComboExcluded fixes are generated when a particular combination of input\ndtypes, index rank and/or layout (as inferred by `data_format`) is given but\nwhich was excluded by the schema API call [exclude_combos](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1340).  For example, \n\n```python\n# from opschema/ops/tf/nn/convolution.py\n# layout 0 here is designated by data_format NCW, NCHW or NCDHW \nop.exclude_combos('input', 'int32', 'i', (1,2), LAYOUT, 0)\nop.exclude_combos('input', 'int32', 'i', 3)\nop.exclude_combos('input', 'bfloat16', 'i', (1,2))\nop.exclude_combos('input', 'bfloat16', 'i', 3, LAYOUT, 0)\n```\n\nIn these situations, TensorFlow sometimes produces verbose error messages that\ndon't mention which parameters have a problem, or an unrelated error message.\n\n```\n## 1    TP      tf.nn.avg_pool: input=[66, 17, 1]:bfloat16, ksize=[7], strides=[50], padding=VALID, data_format=NCW\nTried to squeeze dim index 2 for tensor with 1 dimensions. [Op:Squeeze]\n\nComboExcluded\nThis combination is not implemented: input.dtype in (bfloat16) and [1] input_spatial dimensions\n\n## 89   TP      tf.nn.depth_to_space: input=[78, 312, 1, 36]:qint8, block_size=6, data_format=NHWC\nTensorFlow Exception\nqint8 should be used with data_format NCHW_VECT_C. [Op:DepthToSpace]\n\nComboExcluded\nThis combination is not implemented: input.dtype in (qint8, qint16, qint32) and data_format in (NHWC, NCHW)\n\n## 91   TP      tf.nn.depth_to_space: input=[94, 129, 3, 96]:qint32, block_size=4, data_format=NHWC\nTensorFlow Exception\nCould not find device for node: {{node DepthToSpace}} = DepthToSpace[T=DT_QINT32, block_size=4, data_format=\"NHWC\"]\nAll kernels registered for op DepthToSpace:\n  device='XLA_CPU_JIT'; T in [DT_FLOAT, DT_DOUBLE, DT_INT32, DT_UINT8, DT_INT16, 16005131165644881776, DT_UINT16, DT_COMPLEX128, DT_HALF, DT_UINT32, DT_UINT64]\n  device='XLA_GPU_JIT'; T in [DT_FLOAT, DT_DOUBLE, DT_INT32, DT_UINT8, DT_INT16, 16005131165644881776, DT_UINT16, DT_COMPLEX128, DT_HALF, DT_UINT32, DT_UINT64]\n  device='GPU'; T in [DT_QINT8]\n  device='GPU'; T in [DT_HALF]\n  device='GPU'; T in [DT_FLOAT]\n  device='CPU'; T in [DT_VARIANT]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_RESOURCE]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_STRING]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_BOOL]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_COMPLEX128]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_COMPLEX64]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_DOUBLE]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_FLOAT]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_BFLOAT16]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_HALF]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_INT32]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_INT8]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_UINT8]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_INT16]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_UINT16]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_UINT32]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_INT64]; data_format in [\"NHWC\"]\n  device='CPU'; T in [DT_UINT64]; data_format in [\"NHWC\"]\n [Op:DepthToSpace]\n\nComboExcluded\nThis combination is not implemented: input.dtype in (qint8, qint16, qint32) and data_format in (NHWC, NCHW)\n```\n\n## InsertDim examples\n\nInsertDim fixes are suggested fixes issued by opschema to insert (usually one)\ndimensions to some input tensor.  They occur when the combination of input\ntensor (or other shape-bearing inputs) ranks are not among the allowed rank\ncombinations.  These combinations are logically constructed from\nlower-level constraints on ranks of `OpSchema.schema.Index` objects and\nargument *signatures* (a notion from opschema), explained below.\n\nThe tricky thing about incompatible ranks is that there can be multiple fixes,\nand it is not clear which is most likely the one the user will take.\n\nIn cases like this, TensorFlow makes a strong assumption about which argument\nrank was in error, which can be misleading if it guesses wrong.  Some are quite\nmisleading or incorrect.  \n\n```\n## 7    TP      tf.nn.atrous_conv2d: value=[34, 71, 1, 15]:int32, filters=[79, 1, 11]:int32, rate=14, padding=VALID\nTensorFlow Exception\ndilations should be of length 1, 1 or 3. Received: dilations=[14, 14] of length 2\n\nReceived invalid configuration: value rank = 4, filters rank = 3 and rate rank = 1.  Closest valid configurations:\n\nInsertDim\n            value.shape   filters.shape   rate   return[0].shape\nreceived   [34,71,1,15]       [79,1,11]   [14]\nconfig 1        b i i k      =\u003e f f k l      r           b o o l\n\n=\u003e config 1: add 1 dimension to filters\n\n## 25   TP      tf.nn.convolution: input=[11, 80, 17]:float16, filters=[10, 21]:float16, strides=1, padding=VALID, data_format=NCW, dilations=1\nTensorFlow Exception\nnum_spatial_dims (input.shape.ndims - num_batch_dims - 1) must be one of 1, 2 or 3 but saw 0.  num_batch_dims: 2.\n\nReceived invalid configuration: input rank = 3, filters rank = 2 and data_format = NCW.  Closest valid configurations:\n\nInsertDim\n           input.shape   filters.shape   strides   data_format   dilations   return[0].shape\nreceived    [11,80,17]         [10,21]         1           NCW           1\nconfig 1         b k i        =\u003e f j l         s                         d             b l o\n\n=\u003e config 1: add 1 dimension to filters\n\n## 27   TP      tf.nn.convolution: input=[62, 16]:float16, filters=[38, 19, 4]:float16, strides=1, padding=VALID, data_format=NCW, dilations=1\nTensorFlow Exception\ninput must be 4-dimensional[62,1,16] [Op:Conv2D]\n\nReceived invalid configuration: input rank = 2, filters rank = 3 and data_format = NCW.  Closest valid configurations:\n\nInsertDim\n           input.shape   filters.shape   strides   data_format   dilations   return[0].shape\nreceived       [62,16]       [38,19,4]         1           NCW           1\nconfig 1      =\u003e b k i           f j l         s                         d             b l o\n\n=\u003e config 1: add 1 dimension to input\n\n## 311  TP      tf.nn.bias_add: value=[33, 50]:uint16, bias=[67]:uint16, data_format=NC..\nTensorFlow Exception\nMust provide as many biases as the last dimension of the input tensor: [67] vs. [33,50] [Op:BiasAdd]\n\nReceived invalid configuration: value rank = 2, bias rank = 1 and data_format = NC...  Closest valid configurations:\n\nInsertDim\n           value.shape   bias.shape   data_format   return[0].shape\nreceived       [33,50]         [67]          NC..\nconfig 1      =\u003e b c s            c                           b c s\n\n=\u003e config 1: add 1 dimension to value\n```\n\n## DeleteDim examples\n\nLike the `InsertDim` examples, `DeleteDim` fixes are issued when the submitted\ncombination of ranks is not one of the allowed combinations.  In these\nsituations, multiple suggested fixes may be issued.  This is unfortunately\nrather verbose.  The principle followed here is to search the set of legal\ncombinations, and find those closest in 'edit distance' with the provided\ninputs.  In such cases, alternate `data_format` may also be part of the\nsuggested fix.\n\nIn many of these situations, TensorFlow makes a strong assumption about what is\nwrong, which may not be what the user ultimately wants to fix.\n\n```\n## 7    TP      tf.nn.avg_pool: input=[2, 13, 36, 4]:float16, ksize=[6], strides=[88], padding=VALID, data_format=NCW\nTensorFlow Exception\nValue for attr 'data_format' of \"NCW\" is not in the list of allowed values: \"NHWC\", \"NCHW\"\n        ; NodeDef: {{node AvgPool}}; Op\u003cname=AvgPool; signature=value:T -\u003e output:T; attr=ksize:list(int),min=4; attr=strides:list(int),min=4; attr=padding:string,allowed=[\"SAME\", \"VALID\"]; attr=data_for\nmat:string,default=\"NHWC\",allowed=[\"NHWC\", \"NCHW\"]; attr=T:type,allowed=[DT_HALF, DT_BFLOAT16, DT_FLOAT, DT_DOUBLE]\u003e [Op:AvgPool]\n\nLayout\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived     2 13 36 4       6        88           NCW          2 13 1 0\ntemplate     b  c  i i       k         s          NCHW          b  c o o\n   error                                          ^^^^\n\n=\u003e Change data_format to NCHW\n\nLayout\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived     2 13 36 4       6        88           NCW           2 1 1 4\ntemplate     b  i  i c       k         s          NHWC           b o o c\n   error                                          ^^^^\n\n=\u003e Change data_format to NHWC\n\nReceived invalid configuration: input rank = 4 and data_format = NCW.  Closest valid configurations:\n\nDeleteDim\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived   [2,13,36,4]       6        88           NCW\nconfig 1      =\u003e b c i       k         s                           b c o\n\n=\u003e config 1: remove 1 dimension from input\n\n## 9    TP      tf.nn.avg_pool: input=[2, 36, 1]:float16, ksize=[5, 1], strides=[67], padding=VALID, data_format=NCW\nTensorFlow Exception\nksize should be of length 1, 1 or 3. Received: ksize=[5, 1] of length 2\n\nReceived invalid configuration: input rank = 3, ksize rank = 2 and data_format = NCW.  Closest valid configurations:\n\nDeleteDim\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived      [2,36,1]   [5,1]        67           NCW\nconfig 1         b c i    =\u003e k         s                           b c o\n\n=\u003e config 1: remove 1 dimension from ksize\n\n## 27   TP      tf.nn.conv_transpose: input=[80, 24, 55]:bfloat16, filters=[1, 70, 7, 24]:bfloat16, output_shape=[80, 84, 124], strides=1, padding=VALID, data_format=NCW, dilations=1\nTensorFlow Exception\nTried to squeeze dim index 2 for tensor with 1 dimensions. [Op:Squeeze]\n\nReceived invalid configuration: input rank = 3, filters rank = 4, output_shape rank = 3 and data_format = NCW.  Closest valid configurations:\n\nDeleteDim\n           input.shape   filters.shape   output_shape   strides   data_format   dilations   return[0].shape\nreceived    [80,24,55]     [1,70,7,24]    [80,84,124]         1           NCW           1\nconfig 1         b k i        =\u003e f j k          b l o         s                         d             b l q\n\n=\u003e config 1: remove 1 dimension from filters\n```\n\n## IndexUsage examples\n\nThese fixes are issued when certain dimensions between two different tensors or\nother shapes are supposed to be equal but aren't.  TensorFlow exceptions often\ndo not provide actual names for arguments involved.  opschema automatically\ngenerates a table showing the context of the mismatching dimensions and\nactual parameter names.\n\n```\n## 4    TP      tf.nn.bias_add: value=[52, 63]:int32, bias=[4]:int32, data_format=NC..\nTensorFlow Exception\nMust provide as many biases as the channel dimension of the input tensor: [4] vs. 63 in [52,63] [Op:BiasAdd]\n\nIndexUsage\n           value.shape   bias.shape   data_format   return[0].shape\nreceived         52 63            4          NC..              52 ?\ntemplate          b  c            c                             b c\n   error            ^^            ^                               ^\n\n=\u003e channel (c) has inconsistent dimensions in value and bias. value.shape[1] = 63 and bias.shape[0] = 4\n```\n\n## IndexPred examples\n\nThese fixes are issued when an index predicate is violated.  An index predicate\nis a constraint that the index dimensions (as opposed to rank constraints) must\nsatisfy.  See [Index section](#index-section)).  Such predicates are declared\nusing\n[dims_pred](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1722)\nand related API calls.  For example, `tf.nn.convolution` only accepts strides\nover 1 if dilation is equal to 1, and vice versa.  And, in several other\nconvolution-related ops, the input channel must be evenly divisible by the\nfilter input channel.  These are declared in `tf.nn.convolution` as:\n\n```python\n# from opschema/ops/tf/nn/convolution.py\n# non-component-wise predicate:  all of the components of s must be equal to 1\n# if any one of d is above 1, and vice versa\nop.dims_pred('s-d exclusion', \n        predlib.not_both_over_one,\n        predlib.not_both_over_one_templ, 'sd')\n\n# component-wise predicate - each component of k must be divisible by the \n# corresponding component of j\nop.dims_pred_cw('k % j == 0', predlib.divis_by, predlib.divis_by_t, 'kj')\n```\n\nBy default, computed dimensions (see [Computed\ndimensions](#computed-dimensions) section) automatically have a non-negativity\npredicate assigned to them, which can be seen in opschema error messages and\nautomatically generated documentation.\n\n```\n## 65   TP      tf.nn.convolution: input=[84, 21, 2]:float64, filters=[2, 4, 8]:float64, strides=1, padding=VALID, data_format=NCW, dilations=1\ninput depth must be evenly divisible by filter depth: 21 vs 4 [Op:Conv2D]\n\nIndexPred\n           input.shape   filters.shape   strides   data_format   dilations   return[0].shape\nreceived       84 21 2           2 4 8         1           NCW           1            84 8 1\ntemplate        b  k i           f j l         s                         d             b l o\n   error          ^^               ^\n\ninput_channel (k) = [21] and filter_input_channel (j) = [4].  input_channel must be divisible by filter_input_channel\n\n## 68   TP      tf.nn.convolution: input=[28, 27, 15]:float64, filters=[18, 27, 29]:float64, strides=[1], padding=VALID, data_format=NCW, dilations=1\nComputed output size would be negative: -2 [input_size: 15, effective_filter_size: 18, stride: 1] [Op:Conv2D]\n\nIndexPred\n           input.shape   filters.shape   strides   data_format   dilations   return[0].shape\nreceived      28 27 15        18 27 29         1           NCW           1          28 29 -2\ntemplate       b  k  i         f  j  l         s                         d           b  l  o\n   error                                                                                  ^^\n\noutput_spatial (o) = [-2].  output_spatial must be \u003e= 0\n\nDimensions computed as:\ndilated_filter_spatial = (filter_spatial - 1) * dilations + 1\noutput_spatial = ceil((input_spatial + dilated_filter_spatial - 1) / strides)   [padding = VALID]\n\ng = (f - 1) * d + 1\no = ceil((i + g - 1) / s)   [padding = VALID]\n\n[18] = ([18] - 1) * 1 + 1\n[-2] = ceil(([15] + [18] - 1) / 1)   [padding = VALID]\n\n## 2    TP      tf.nn.conv_transpose: input=[46, 17, 31]:bfloat16, filters=[39, 3, 17]:bfloat16, output_shape=[46, 196, 69], strides=1, padding=VALID, data_format=NCW, dilations=1\nTensorFlow Exception\nTried to squeeze dim index 2 for tensor with 1 dimensions. [Op:Squeeze]\n\nIndexPred\n           input.shape   filters.shape   output_shape   strides   data_format   dilations   return[0].shape\nreceived      46 17 31         39 3 17      46 196 69         1           NCW           1         46 196 69\ntemplate       b  k  i          f j  k       b   l  o         s                         d          b   l  q\n   error                          ^            ^^^                                                   ^^^\n\noutput_channel (l) = [196] and filter_output_channel (j) = [3].  output_channel must be divisible by filter_output_channel\n\n## 3    TP      tf.nn.conv_transpose: input=[46, 17, 2]:bfloat16, filters=[39, 28, 17]:bfloat16, output_shape=[46, 196, 69], strides=1, padding=VALID, data_format=NCW, dilations=1\nTensorFlow Exception\nTried to squeeze dim index 2 for tensor with 1 dimensions. [Op:Squeeze]\n\nIndexPred\n           input.shape   filters.shape   output_shape   strides   data_format   dilations   return[0].shape\nreceived       46 17 2        39 28 17      46 196 69         1           NCW           1         46 196 40\ntemplate        b  k i         f  j  k       b   l  o         s                         d          b   l  q\n   error                                           ^^                                                    ^^\n\noutput_spatial_declared (o) = [69] and output_spatial_computed (q) = [40].  output_spatial_declared must equal output_spatial_computed\n\nDimensions computed as:\nstrided_input_spatial = (input_spatial - 1) * strides + 1\ndilated_filter_spatial = (filter_spatial - 1) * dilations + 1\noutput_spatial_computed = strided_input_spatial + dilated_filter_spatial - 1   [padding = VALID]\n\nn = (i - 1) * s + 1\ng = (f - 1) * d + 1\nq = n + g - 1   [padding = VALID]\n\n[2] = ([2] - 1) * 1 + 1\n[39] = ([39] - 1) * 1 + 1\n[40] = [2] + [39] - 1   [padding = VALID]\n```\n\n## Layout examples\n\nLayout fixes are issued by opschema when the closest valid configuration either\ninvolves a different layout or a different value for the layout-associated\nargument (usually `data_format`).  In many examples, TensorFlow properly\ndetects that rank combinations, but does not inspect the dimensions to\ndetermine the most plausible intended layout.  opschema actively checks both\nlayouts against actual dimensions for compatibility and suggests any that are\nwithin a given 'edit distance'.\n\nIn other cases, the rank implied by the provided data_format is correct.  Since\nthe data_format parameter controls the interpretation of indexes, using the\nwrong data_format might lead to an apparent error in index predicates.  In\nexample 914 below, TensorFlow assumes that the input depth and filter depth\nvalues are erroneous, but this could also be a case of the wrong data_format\nprovided.\n\nopschema has a flexible method for searching most plausible fixes based on a\nheuristic assignment of edit distance scores.  These could be adjusted, as well\nas thresholds for which fixes to report.\n\n```\n## 155  TP      tf.nn.conv_transpose: input=[25, 28, 44]:float16, filters=[50, 2, 28]:float16, output_shape=[25, 80, 93], strides=1, padding=VALID, data_format=NCDHW, dilations=1\nTensorFlow Exception\n`data_format` must be 'NWC' or 'NCW'. Received: data_format=NCDHW\n\nLayout\n           input.shape   filters.shape   output_shape   strides   data_format   dilations   return[0].shape\nreceived      25 28 44         50 2 28       25 80 93         1         NCDHW           1          25 80 93\ntemplate       b  k  i          f j  k        b  l  o         s           NCW           d           b  l  q\n   error                                                                ^^^^^\n\n=\u003e Change data_format to NCW\n\n## 914  TP      tf.nn.convolution: input=[99, 17, 1, 57]:int32, filters=[64, 1, 19, 8]:int32, strides=1, padding=SAME, data_format=NCHW, dilations=1\ninput depth must be evenly divisible by filter depth: 17 vs 19 [Op:Conv2D]\n\nLayout\n           input.shape   filters.shape   strides   data_format   dilations   return[0].shape\nreceived    99 17 1 57       64 1 19 8         1          NCHW           1         99 17 1 8\ntemplate     b  i i  k        f f  j l       s s          NHWC         d d          b  o o l\n   error                                                  ^^^^\n\n=\u003e Change data_format to NHWC\n```\n\n## False Negatives\n\nFalse negative results occur when opschema does not detect a violation of\nschema constraints, yet TensorFlow raises an exception.  Depending on your\ninterpretation, this could be considered a bug in the schema itself or\nunintended behavior from TensorFlow.  Some examples are shown below.\n\n```\n## 4    FN      tf.nn.convolution: input=[87, 80, 3]:float16, filters=[1, 2, 3]:float16, strides=1, padding=VALID, data_format=NCW, dilations=1\nNo algorithm worked! [Op:Conv2D]\n\nNone\n\n## 1729 FN      tf.nn.convolution: input=[82, 1, 2, 42]:int32, filters=[2, 21, 9]:int32, strides=1, padding=VALID, data_format=NWC, dilations=[1]\nThe Conv2D op currently does not support grouped convolutions for integer types. A grouped convolution was attempted to be run because the input depth of 42 does not match the filter input depth of 21 [Op:Conv2D]\n\nNone\n\n## 1    FN      tf.nn.conv_transpose: input=[46, 17, 31]:bfloat16, filters=[39, 28, 17]:bfloat16, output_shape=[46, 196, 69], strides=1, padding=VALID, data_format=NCW, dilations=1\nTensorFlow Exception\nTried to squeeze dim index 2 for tensor with 1 dimensions. [Op:Squeeze]\n\nNone\n\n```\n\n## Some notable earlier examples with interpretations (different format)\n\n```\n## 59   TP      tf.nn.convolution: input=[69, 45]:float32, filters=[59, 23, 2]:float32, strides=1, padding=VALID, data_format=NCW, dilations=1\ninput must be 4-dimensional[69,1,45] [Op:Conv2D]\n\nReceived invalid configuration: input rank = 2, filters rank = 3 and data_format = NCW.  Closest valid configurations:\n\n           input.shape   filters.shape   strides   data_format   dilations   return[0].shape\nreceived       [69,45]       [59,23,2]         1           NCW           1\nconfig 1      =\u003e b k i           f j l         s                         d             b l o\n\n=\u003e config 1: add 1 dimension to input\n```\n\nHere TensorFlow's message is contradictory and nearly uninterpretable.  It\nseems to claim that 'input' has a shape [69,1,45] which it does not actually\nhave.\n\n```\n## 98   FP      tf.nn.convolution: input=[83, 5, 90]:float32, filters=[90, 5, 25]:float32, strides=1, padding=VALID, data_format=NCHW, dilations=1\nNone\n\n           input.shape   filters.shape   strides   data_format   dilations   return[0].shape\nreceived       83 5 90         90 5 25         1          NCHW           1           83 25 1\ntemplate        b k  i          f j  l         s           NCW           d            b  l o\n   error                                                  ^^^^\n\n=\u003e Change data_format to NCW\n```\n\nHere, TensorFlow succeeds, performing a 1D convolution successfully, even\nthough `data_format` was provided incorrectly as `NCHW`, contrary to the\ndocumentation.\n\n```\n## 1868 FP      input=[99, 1, 90, 31]:float16, filters=[40, 15, 18]:float16, strides=[4], padding=SAME, data_format=NCW, dilations=1\nNone\n\nReturn tensor 0 was expected to have shape [99, 1, 18, 8] but was [99, 1, 18, 31]\n```\n\nHere is another false positive case in which TensorFlow does not raise an\nexception.  opschema flags it because the return tensor didn't have the\nexpected shape.  \n\n```\n## 1    TP      tf.nn.avg_pool: input=[66, 17, 1]:bfloat16, ksize=[7], strides=[50], padding=VALID, data_format=NCW\nTried to squeeze dim index 2 for tensor with 1 dimensions. [Op:Squeeze]\n\nThis combination is not implemented: input.dtype in (bfloat16) and [1] input_spatial dimensions\n```\n\nHere, TensorFlow's exception does not give the user any clue what went wrong.\nopschema's error message in my opinion could be improved, but is reasonably\nclear.  Perhaps it could be augmented with a template table as in previous\nexamples.\n\n```\n## 9    TP      tf.nn.avg_pool: input=[2, 36, 1]:float16, ksize=[5, 1], strides=[67], padding=VALID, data_format=NCW\nksize should be of length 1, 1 or 3 but was 2\n\nReceived invalid configuration: input rank = 3, ksize rank = 2 and data_format = NCW.  Closest valid configurations:\n\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived      [2,36,1]   [5,1]        67           NCW\nconfig 1         b c i    =\u003e k         s                           b c o\n\n=\u003e config 1: remove 1 dimension from ksize\n```\n\nTensorFlow guesses that the rank of ksize is the problem, but suggests '1, 1,\nor 3', which doesn't make much sense.\n\n```\n## 13   TP      tf.nn.avg_pool: input=[91, 19, 5]:float16, ksize=[5], strides=[14, 1], padding=VALID, data_format=NCW\nstrides should be of length 1, 1 or 3 but was 2\n\nReceived invalid configuration: input rank = 3, strides rank = 2 and data_format = NCW.  Closest valid configurations:\n\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived     [91,19,5]       5    [14,1]           NCW\nconfig 1         b c i       k      =\u003e s                           b c o\n\n=\u003e config 1: remove 1 dimension from strides\n```\n\nHere TensorFlow's exception seems to have the same problem as in example 9.\nAlso, the documentation contains a similar confusing message about `ksize` and\n`strides` parameters:\n\n\t  strides   An int or list of ints that has length 1, N or N+2. The stride of the \n              sliding window for each dimension of the input tensor.\n\nIt should read '1, 2, or 3'.\n\n```\n## 42   FP      tf.nn.avg_pool: input=[37, 40, 6]:float16, ksize=[8], strides=[98], padding=VALID, data_format=NCHW\nNone\n\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived       37 40 6       8        98          NCHW           37 40 0\ntemplate        b  c i       k         s           NCW            b  c o\n   error                                          ^^^^\n\n=\u003e Change data_format to NCW\n\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived       37 40 6       8        98          NCHW            37 1 6\ntemplate        b  i c       k         s           NWC             b o c\n   error                                          ^^^^\n\n=\u003e Change data_format to NWC\n\nReceived invalid configuration: input rank = 3 and data_format = NCHW.  Closest valid configurations:\n\n           input.shape   ksize   strides   data_format   return[0].shape\nreceived     [37,40,6]       8        98          NCHW\nconfig 1    =\u003e b c i i       k         s                         b c o o\n\n=\u003e config 1: add 1 dimension to input\n```\n\nHere, as in `tf.nn.convolution` example 98, TensorFlow allows `data_format =\nNCHW` in contradiction with documentation.  opschema is admittedly trying a bit\ntoo hard and is too verbose (probably needs to be pared down).\n\n```\n## 51   TP      input=[63, 34, 2]:float16, ksize=[3], strides=[90], padding=VALID, data_format=NHWC\nCan not squeeze dim[2], expected a dimension of 1, got 0 [Op:Squeeze]\n\n\u003copschema response omitted\u003e\n```\n\nTensorFlow's error message is not useful at all here.\n\n\n```\n## 17   TP      tf.nn.depth_to_space: input=[90, 306, 1, 51]:int32, block_size=1, data_format=NHWC\nValue for attr 'block_size' of 1 must be at least minimum 2\n        ; NodeDef: {{node DepthToSpace}}; Op\u003cname=DepthToSpace; signature=input:T -\u003e output:T; attr=T:type; attr=block_size:int,min=2; attr=data_format:string,default=\"NHWC\",allowed=[\"NHWC\", \"NCHW\", \"NCHW_VECT_C\"]\u003e [Op:DepthToSpace]\n\nArgument 'block_size' expected to be an integer \u003e= 2\n```\n\nThe first part of this message is not bad, except that it is confusing to call\nit attr 'block_size'.  And, the remaining part is just visual noise.\n\n\n```\n## 7    TP      tf.nn.depth_to_space: input=[40, 36]:int8, block_shape=[31, 1], paddings=[11], [15]\ninput rank should be \u003e= 3 instead of 2 [Op:SpaceToBatchND]\n\nReceived invalid configuration: input rank = 2, block_shape rank = 2, paddings.0 rank = 1 and paddings.1 rank = 1.  Closest valid configurations:\n\n           input.shape   block_shape   return[0].shape\nreceived       [40,36]        [31,1]\nconfig 1           b i          =\u003e k               p o\n\n=\u003e config 1: remove 1 dimension from block_shape\n```\n\nComparing the received signatures with closest valid signatures:\n\n                input  block_shape  paddings.0  paddings.1\n    received    bi     kk           s           e\n    config 1    bi     k            s           e\n    config 2    bii    kk           ss          ee\n\nopschema suggests the closest valid one (config 1) which implies that\nblock_shape has one too many dimensions.  The other possibility is that it was\ncorrect, but that both input and paddings are missing a dimension.\n\nTensorFlow assumes the latter, which may be too strong of an assumption.\n\n```\n## 8    TP      tf.nn.depth_to_space: input=[47, 34]:int8, block_shape=[], paddings=[1], [23]\npaddings should have shape [0, 2] instead of [1,2] [Op:SpaceToBatchND]\n\nReceived invalid configuration: input rank = 2, block_shape rank = 0, paddings.0 rank = 1 and paddings.1 rank = 1.  Closest valid configurations:\n\n           input.shape   block_shape   return[0].shape\nreceived       [47,34]            []\nconfig 1           b i          =\u003e k               p o\n\n=\u003e config 1: add 1 dimension to block_shape\n```\n\nAbove, 'block_shape' was incorrectly provided with rank 0, when it must be\nbetween 1 and 3.  But, TensorFlow incorrectly suggests to change the 'paddings'\nparameter rank to match that of 'block_shape', leading to a nonsensical\nsuggestion.\n\n```\n## 21   TP      tf.nn.depth_to_space: input=[1, 17, 4]:int16, block_shape=[47], paddings=[34], [9]\npadded_shape[0]=60 is not divisible by block_shape[0]=47 [Op:SpaceToBatchND]\n\n           input.shape   block_shape   return[0].shape\nreceived        1 17 4            47            47 1 4\ntemplate        b  i r             k             p o r\n   error                          ^^\n\npadded_input_spatial (j) = [60] and block_shape (k) = [47].  padded_input_spatial must be divisible by block_shape\n\nDimensions computed as:\noutput_batch = product(block_shape) * batch\npadded_input_spatial = padding_start + input_spatial + padding_end\n\np = product(k) * b\nj = s + i + e\n\n[47] = product([47]) * 1\n[60] = [34] + [17] + [9]\n```\n\nHere, TensorFlow's error message is not bad.  However, there is no mechanism\nfor synching the documentation with the constraint that is violated.  On the\nother hand, opschema's 'explain' (detailed below) programmatically generates\ndocumentation using the same names used in the generated error messages:\n\n```\nComputed dimensions\n\noutput_batch = product(block_shape) * batch\npadded_input_spatial = padding_start + input_spatial + padding_end\noutput_spatial = padded_input_spatial // block_shape\n\np = product(k) * b\nj = s + i + e\no = j // k\n\nIndex predicates\n\npadded_input_spatial must be divisible by block_shape\npadded_input_spatial must be \u003e= 0\noutput_spatial must be \u003e= 0\noutput_batch must be \u003e= 0\n```\n\nusing names which are programmatically guaranteed to appear in the run-time\nerror messages.\n\n# Schema\n\n`opschema` defines an op schema using a few basic concepts common to all ops.\nTo best illustrate these I'll illustrate them with the example of the\n`tf.nn.convolution` schema.\n\n    python -m opschema.cl explain tf.nn.convolution -i\n\n```\nSchema for tf.nn.convolution\n\nIndexes\n\nIndex  Description           \nb      batch                 \ni      input spatial         \nf      filter spatial        \ng      dilated filter spatial\ns      strides               \nd      dilations             \nk      filter input channel\nj      output filter         \nl      output channel        \no      output spatial        \n\nSignatures\n\ninput  filters  strides  dilations  return[0]  data_format             \nbki    fjl      s        d          blo        ['NCW', 'NCHW', 'NCDHW']\nbik    fjl      s        d          bol        ['NWC', 'NHWC', 'NDHWC']\n\nIndex ranks\n\nrank(b) in [1, 5]     \nrank(i) in [1, 3]     \nrank(f) = rank(i)     \nrank(g) = rank(i)     \nrank(s) = rank(i)     \nrank(d) = rank(i)     \nrank(k) = 1           \nrank(j) = 1           \nrank(l) = 1           \nrank(o) = rank(i)     \n\nComputed dimensions\n\ndilated_filter_spatial = (filter_spatial - 1) * dilations + 1\noutput_spatial = ceil(input_spatial / strides)   [padding = SAME]\noutput_spatial = ceil((input_spatial + dilated_filter_spatial - 1) / strides)   [padding = VALID]\n\ng = (f - 1) * d + 1\no = ceil((i + g - 1) / s)   [padding = VALID]\no = ceil(i / s)   [padding = SAME]\n\nIndex predicates\n\ndilated_filter_spatial must be \u003e= 0\noutput_spatial must be \u003e= 0\nstrides and dilations dimensions cannot both contain an element over 1\ninput_channel must be divisible by output_filter\n\ng must be \u003e= 0\no must be \u003e= 0\ns and d dimensions cannot both contain an element over 1\nk must be divisible by j\n\nDType Rules\n\ninput.dtype in (int32, float16, float32, float64, bfloat16)\nfilters.dtype = input.dtype\n\nExcluded DType Combos\n\ninput.dtype  rank(i)  layout\nint32        1,2      0     \nint32        3        *     \nbfloat16     1,2      *     \nbfloat16     3        0     \n\nInventory\n\ninput.shape  input.dtype  filters.shape  filters.dtype  strides  data_format  dilations  return[0].shape\nbki          float16      fjl            float16        s        NCW          d          blo            \nbki          float32      fjl            float32        s        NCW          d          blo            \nbki          float64      fjl            float64        s        NCW          d          blo            \nbik          int32        fjl            int32          s        NWC          d          bol            \nbik          float16      fjl            float16        s        NWC          d          bol            \nbik          float32      fjl            float32        s        NWC          d          bol            \nbik          float64      fjl            float64        s        NWC          d          bol            \nbki          float16      fjl            float16        s        NCW          d          blo            \nbki          float32      fjl            float32        s        NCW          d          blo            \nbki          float64      fjl            float64        s        NCW          d          blo            \nbik          int32        fjl            int32          s        NWC          d          bol            \nbik          float16      fjl            float16        s        NWC          d          bol            \nbik          float32      fjl            float32        s        NWC          d          bol            \nbik          float64      fjl            float64        s        NWC          d          bol            \nbkii         float16      ffjl           float16        ss       NCHW         dd         bloo           \nbkii         float32      ffjl           float32        ss       NCHW         dd         bloo           \n...\n```\n\n`opschema` uses three abstractions to define the schema:  *index*, *signature*,\nand *layout*.  The first section lists the indices:\n\n\n## Index section\n\n```bash\nIndex  Description           \nb      batch                 \ni      input spatial         \nf      filter spatial        \ng      dilated filter spatial\ns      strides               \nd      dilations             \nk      input channel         \nj      filter input channel \nl      output channel        \no      output spatial        \n```\nopschema Indexes are declared with\n[add_index](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L912) \nas in:\n\n```python\n# excerpt from opschema/ops/tf/nn/convolution.py\n# declare an index called 'batch' which can range in rank from 1 to 5\nop.add_index('b', 'batch', (1,5))\nop.add_index('i', 'input spatial', (1,3))\n\n# declare index 'f' to have rank equivalent to index 'i'\nop.add_index('f', 'filter spatial', 'i')\n...\n```\n\nopschema `Index` objects represent shaped quantities.  They are not always\ninstantiated directly in input or output tensors, however.  Any quantities that\nparticipate in computations that involve shapes, even intermediate\ncalculations, can be declared as `Index` entities.  In the example above,\n'strides' and 'dilations' are ordinary parameters, while 'dilated filter\nspatial' is an intermediate index that does not appear in any inputs or outputs\nof the op.\n\n\n## Signatures section\n\n```bash\nSignatures\n\ninput  filters  strides  dilations  return[0]  data_format             \nbki    fjl      s        d          blo        ['NCW', 'NCHW', 'NCDHW']\nbik    fjl      s        d          bol        ['NWC', 'NHWC', 'NDHWC']\n```\n\nThis section shows a table with one *layout* for each row.  Each column\nrepresents a shape-bearing parameter (which may be a tensor, but may not).  The cells in\nthe row define *signatures*, which are concatenations of the single letter\ncodes for `Index` objects.  For example, the 'filters' parameter has signature\n'fjl', meaning that its shape is interpreted as a set of dimensions 'filter\nspatial', then 'filter input channel', then 'output channel'.\n\nThe individual arguments are registered with the schema depending on the kind\nof argument.  Input tensors are registered with [arg_tensor]( https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1512)\nand return tensors with [return_tensor]( https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1788).\nThe signatures are declared with these API calls, and the layouts are\nassociated with the `data_format` parameter using the API call \n[arg_layout](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1402).\n\nThe OpSchema API calls are:\n\n```python\n# excerpt from opschema/ops/tf/nn/convolution.py\nformats = {\n        'NCW': (0, 1), # layout 0, rank(i) = 1\n        'NCHW': (0, 2), # etc...\n        'NCDHW': (0, 3),\n        'NWC': (1, 1),\n        'NHWC': (1, 2),\n        'NDHWC': (1, 3),\n        None: (1, None),  # default layout is layout 1, regardless of rank(i)\n        }\n\n# argument 'data_format' determines the layout according to the 'formats' map\n# and the rank of index 'i'\nop.arg_layout('data_format', formats, 'i')\n\n# tensor 'input' is registered with signatures for each layout\nop.arg_tensor('input', 'bki', 'bik')\nop.arg_tensor('filters', 'fjl')\n```\n\n## Index ranks\n\n\n```bash\nIndex ranks\n\nrank(b) in [1, 5]     \nrank(i) in [1, 3]     \nrank(f) = rank(i)     \nrank(g) = rank(i)     \nrank(s) = rank(i)     \nrank(d) = rank(i)     \nrank(k) = 1           \nrank(j) = 1           \nrank(l) = 1           \nrank(o) = rank(i)     \n```\n\nThe Index ranks section defines rank constraints for each `Index` object.  An\nIndex rank means the same as for a tensor, but for a subset of semantically\nrelated indices.  For instance, 'filter.rank' is equal to `rank(f) + rank(j) +\nrank(l)`.  According to the above constraints, this would imply it could range\nfrom 3 to 5.  All of the above rank constraints are determined during index\ncreation, but an additional API function [limit_ranks](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1259)\ncan be used.\n\n## Computed dimensions\n\n\n```bash\nComputed dimensions\n\ndilated_filter_spatial = (filter_spatial - 1) * dilations + 1\noutput_spatial = ceil(input_spatial / strides)   [padding = SAME]\noutput_spatial = ceil((input_spatial + dilated_filter_spatial - 1) / strides)   [padding = VALID]\n\ng = (f - 1) * d + 1\no = ceil((i + g - 1) / s)   [padding = VALID]\no = ceil(i / s)   [padding = SAME]\n```\n\nThe Computed dimensions section shows the formulas registered for Computed Indexes.\nThe formulas are shown in snake-cased\nform and single-letter-code form.  For formulas that depend on other op\nparameters (in this case the 'padding' parameter), the variants of the formulas\nare shown.  These formulas are used both to compute valid inputs during error\nchecking, and to generate readable formulas for context in error messages.\n\nComputed dimensions are registered with the API call [comp_dims](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1173)\nand related variants.\n\n```python\n# excerpt from opschema/ops/tf/nn/convolution.py\nfrom opschema.complib import dilate, dilate_t, strided_conv, strided_conv_t\n\n# Index 'g' (dilated filter spatial) is computed using the dilate function\n# from f (filter spatial) and d (dilation)\nop.comp_dims_cw('g', dilate, dilate_t, 'fd') \n\n# Index 'o' (output spatial) is computed using the strided_conv function from \n# index 'i' (input spatial), 'g' (dilated filter spatial), and 's' (stride)\nop.comp_dims_cw('o', strided_conv, strided_conv_t, 'igs', 'padding')\n```\n\nBecause certain formulas recur in many ops, such functions may be found in\n`opschema/complib.py`.  A numeric version operating on integers and a template\nversion interpolating string representations must be provided.  For example:\n\n```python\n# excerpt from opschema/complib.py\ndef strided_conv(i, f, s, padding):\n    if padding == 'VALID':\n        return ceildiv(i - f + 1, s)\n    else:\n        return ceildiv(i, s)\n\ndef strided_conv_t(i, f, s, padding):\n    if padding == 'VALID':\n        return f'ceil(({i} + {f} - 1) / {s})'\n    else:\n        return f'ceil({i} / {s})' \n```\n\nBecause the schema overall is defined as a python function, any custom compute\nfunctions may be defined as local functions as well.  Placing them in\n`opschema/complib.py` is just a convenience.\n\n## Index Predicates\n\n```bash\nIndex predicates\n\ndilated_filter_spatial must be \u003e= 0\noutput_spatial must be \u003e= 0\nstrides and dilations dimensions cannot both contain an element over 1\ninput_channel must be divisible by filter_input_channel \n\ng must be \u003e= 0\no must be \u003e= 0\ns and d dimensions cannot both contain an element over 1\nk must be divisible by j\n```\n\nPredicate functions may be registered on individual or combinations of indices.\nA non-negativity predicate is automatically registered on all computed indices.\nIn the above example, these are 'dilated filter spatial' and 'output spatial'.\nThe schema author may register additional predicates.  In the case of\n`tf.nn.convolution`, 'input channel' must be disivible by 'filter input\nchannel'.  In fact this is not documented, but it is empirically true. \n\nPredicates are registered with API call\n[dims_pred](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1722)\nand its component-wise variant, as follows:\n\n```python\n# excerpt from opschema/ops/tf/nn/convolution.py\n# only stride or dilation components can be over 1, not both (this is documented)\nop.dims_pred('s-d exclusion', \n        predlib.not_both_over_one,\n        predlib.not_both_over_one_templ, 'sd')\n\n# input channel must be disivible by filter input channel (not documented)\nop.dims_pred_cw('k % j == 0', predlib.divis_by, predlib.divis_by_t, 'kj')\n```\n\n## DType constraints\n\n```bash\nDType Rules\n\ninput.dtype in (int32, float16, float32, float64, bfloat16)\nfilters.dtype = input.dtype\n\nExcluded DType Combos\n\ninput.dtype  rank(i)  layout\nint32        1,2      0     \nint32        3        *     \nbfloat16     1,2      *     \nbfloat16     3        0     \n```\n\nConstraints on allowed DTypes are given first as a set of broad rules, and then\nspecific exclusions.  The DType Rules can be one of two forms - either specify\nthat some tensor can take on certain dtypes, or specify that a tensor dtype\nmust be the same as another tensor.\n\nThe Excluded DType Combos section specifies combinations of dtype, index rank,\nand possibly layout which are excluded.  Usually this is done because such\ncombinations are not implemented.  In the above example, `int32` Conv1D and\nConv2D are not implemented specifically for layout 0, which means data_formats\n'NCW', 'NCHW'.\n\nDType constraints are declared using API calls \n[valid_dtypes](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1282),\n[equate_dtypes](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1318),\n[exclude_combos](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1340)\n\nas shown here:\n\n```python\n# excerpt from opschema/ops/tf/nn/convolution.py\nop.valid_dtypes('input', ('int32', 'float', 'bfloat16'))\nop.equate_dtypes('filters', 'input')\nop.exclude_combos('input', 'int32', 'i', (1,2), LAYOUT, 0)\nop.exclude_combos('input', 'int32', 'i', 3)\nop.exclude_combos('input', 'bfloat16', 'i', (1,2))\nop.exclude_combos('input', 'bfloat16', 'i', 3, LAYOUT, 0)\n```\n\n## Other Constraints\n\nThere are other relationships between inputs in certain TensorFlow ops.  For\nexample, with `tf.gather_nd`, the last dimension of the `indices` shape\ndetermines the rank of the 'read location' (r) index.  This is declared using\nthe API function [rank_dims_constraint](https://github.com/hrbigelow/opschema/blob/master/opschema/schema.py#L1711).\nFor a complete list of API functions, see `opschema.schema.OpSchema` class.\n\n# Computation Graphs\n\nThe schema API internally builds four computation graphs.  They can be viewed\nwith:\n\n    python -m opschema.cl graph OP_PATH OUT_DIR\n\nThis will produce pdf files `OUT_DIR/OP_PATH.{pred,gen,inf,dims}.pdf`.  A\ncomputation graph here has the usual meaning - nodes wrap functions, and\nthe parents of a node provide the inputs to the function.  Nodes without\nparents wrap functions that take no inputs.  Evaluating the graph as a whole\nmeans evaluating the functions in valid topological order.\n\n## Generative Graph\n\nTwo specializations of this idea are used in opschema.  A ***generative\ngraph*** has nodes which wrap generator functions, which are provided in\n[opschema/generators.py](https://github.com/hrbigelow/opschema/blob/master/opschema/generators.py).\nEach function will yield zero or more items, depending on the inputs it\nreceives.  The graph as a whole becomes a generator which yields value sets,\none value corresponding to each node.  This notion can be seen as a\ngeneralization of `itertools.product`, which can be implemented as a generative\ngraph of fully disconnected nodes with no parents.\n\nThe `gen` graph is responsible for generating op input sets.  A subset of its\nnodes represent parameters of the op, while another subset represent hidden\nstates which control relationships between them.\n\nIn addition, the `gen` graph is used during input evaluation.  It generates\npotential interpretations of the inputs, and for each interpretation, measures\nedit distance against the provided inputs.  In this way, opschema is capable of\ndetecting multiple potential fixes for a given set of erroneous inputs.  The\nthresholds and edit distance scoring heuristics could be adjusted.\n\n## Predicate Graph\n\nThe second specialization is a ***predicate graph***.  Its nodes wrap predicate\nfunctions defined in\n[opschema/predicates.py](https://github.com/hrbigelow/opschema/blob/master/opschema/predicates.py)\nAs before, nodes with no parents hold predicate functions (function objects\nactually) which return a tuple `pred`, `data`.  If `pred` is True, `data` is\npassed on to the node's children as an input argument and graph evaluation\nproceeds.  If `pred` is False,\n`data` is an instance of `ErrorReport` which holds information about the\nfailure, and graph evaluation halts.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhrbigelow%2Fopschema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhrbigelow%2Fopschema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhrbigelow%2Fopschema/lists"}