Summary
tf.keras.Input(shape=json.loads("[32]")) produces a tensor whose dtype Ariadne resolves (FLOAT32, the Keras default) but whose shape it cannot trace (emits null dims, lattice ⊤ for shape). The shape information is recoverable in principle—json.loads("[32]") is a pure function on a compile-time-constant string, evaluable at analysis time.
Consider extending the analyzer to:
- Recognize
json.loads(s) where s is a string-literal argument.
- Evaluate the parse at analysis time (Python's
json.loads is deterministic on constant input).
- Track the resulting list/dict structure through subsequent shape extractions.
This would let tf.keras.Input(shape=json.loads(...)) resolve to a concrete shape rather than fall back to ⊤.
Reproducer
import json
import tensorflow as tf
shape = json.loads("[32]")
x = tf.keras.Input(shape=shape)
Current output for x's inferred TensorType:
{TensorType(cellType="float32", dims=null)}
Expected after enhancement (modulo the batch-dimension convention tf.keras.Input already applies):
{TensorType(cellType="float32", dims=[SymbolicDim("?"), NumericDim(32)])}
Why It Matters
Precision wins. Downstream consumers (refactorings, type-inference pipelines) that drop ⊤ entries today would gain a concrete shape, reducing the "shape unknown" rate on real code. The enhancement does not change the lattice contract—shape-⊤ remains the correct fallback for genuinely opaque sources; this narrows the set of sources that count as opaque.
Scope
- Limited to
json.loads(s) where s is a compile-time string literal (CAst constant). Dynamic strings remain shape-⊤.
- No need to model
json.dumps or other JSON APIs—asymmetric use case (shape strings parsed in, rarely serialized out).
pickle.loads, ast.literal_eval, yaml.safe_load are similar candidates but out of scope; file separately if useful.
Cross-Refs
ponder-lab/Hybridize-Functions-Refactoring#491 tracks the consumer-side coordination: Hybridize currently uses json.loads("[32]") in a regression fixture to intentionally defeat shape inference and exercise the shape-⊤ branch. If this enhancement lands, that fixture needs a replacement (a more durable opaque-shape source) before bumping the Ariadne dep.
Summary
tf.keras.Input(shape=json.loads("[32]"))produces a tensor whose dtype Ariadne resolves (FLOAT32, the Keras default) but whose shape it cannot trace (emitsnulldims, lattice ⊤ for shape). The shape information is recoverable in principle—json.loads("[32]")is a pure function on a compile-time-constant string, evaluable at analysis time.Consider extending the analyzer to:
json.loads(s)wheresis a string-literal argument.json.loadsis deterministic on constant input).This would let
tf.keras.Input(shape=json.loads(...))resolve to a concrete shape rather than fall back to ⊤.Reproducer
Current output for
x's inferredTensorType:Expected after enhancement (modulo the batch-dimension convention
tf.keras.Inputalready applies):Why It Matters
Precision wins. Downstream consumers (refactorings, type-inference pipelines) that drop ⊤ entries today would gain a concrete shape, reducing the "shape unknown" rate on real code. The enhancement does not change the lattice contract—shape-⊤ remains the correct fallback for genuinely opaque sources; this narrows the set of sources that count as opaque.
Scope
json.loads(s)wheresis a compile-time string literal (CAst constant). Dynamic strings remain shape-⊤.json.dumpsor other JSON APIs—asymmetric use case (shape strings parsed in, rarely serialized out).pickle.loads,ast.literal_eval,yaml.safe_loadare similar candidates but out of scope; file separately if useful.Cross-Refs
ponder-lab/Hybridize-Functions-Refactoring#491 tracks the consumer-side coordination: Hybridize currently uses
json.loads("[32]")in a regression fixture to intentionally defeat shape inference and exercise the shape-⊤ branch. If this enhancement lands, that fixture needs a replacement (a more durable opaque-shape source) before bumping the Ariadne dep.