JAFF Types
The jaff_types module provides specialized data structures for representing indexed mathematical expressions and collections used in code generation.
Overview
This module defines two primary classes that work together to organize and structure code generation output:
IndexedValue- An immutable container representing a value associated with array indicesIndexedList- A type-safe list that only acceptsIndexedValueelements
These types are used extensively in the Codegen class to structure CSE (Common Subexpression Elimination) results, array assignments, and other indexed expressions.
from jaff.jaff_types import IndexedValue, IndexedList
# Create an indexed value
iv = IndexedValue([0], "x + y")
print(iv.indices) # [0]
print(iv.value) # "x + y"
# Create a list of indexed values
items = IndexedList([
IndexedValue([0], "2*x"),
IndexedValue([1], "y**2")
])
Classes
IndexedValue
class IndexedValue(tuple):
"""
Immutable container for an indexed value with multi-dimensional indexing.
"""
An immutable tuple subclass that pairs one or more array indices with a value or expression. Used to represent statements like array[i] = expression or matrix[i][j] = expression.
Constructor
IndexedValue()
Create a new IndexedValue.
Parameters:
indices(list[int]): List of integer indices for array access (e.g.,[0],[2, 3])value(Any): The value/expression to associate with these indices
Returns:
IndexedValue: New indexed value instance
Raises:
TypeError: Ifindicesis not a list
Examples:
# 1D array indexing
iv1 = IndexedValue([0], "x + y")
# Represents: array[0] = x + y
# 2D array indexing
iv2 = IndexedValue([2, 3], "sin(theta)")
# Represents: matrix[2][3] = sin(theta)
# Complex expression
iv3 = IndexedValue([5], "k[0] * n[1] * n[2]")
# Represents: rate[5] = k[0] * n[1] * n[2]
Properties
indices
Get the list of integer indices.
Returns:
list[int]: List of indices
Example:
value
Get the associated value or expression.
Returns:
Any: The value/expression (can be string, number, list, etc.)
Example:
iv = IndexedValue([0], "x + y")
print(iv.value) # "x + y"
iv2 = IndexedValue([1], [1, 2, 3])
print(iv2.value) # [1, 2, 3]
Methods
__repr__()
Return detailed string representation.
Returns:
str: String representation showing both indices and value
Example:
__str__()
Return human-readable string representation.
Returns:
str: Formatted string showing indices → value mapping
Example:
iv = IndexedValue([0], "x + y")
print(str(iv))
# Output: [0] -> x + y
iv2 = IndexedValue([1], [1, 2, 3])
print(str(iv2))
# Output: [1] -> [1, 2, 3]
Immutability
IndexedValue is immutable (inherits from tuple), so indices and values cannot be changed after creation:
iv = IndexedValue([0], "value")
# iv.indices[0] = 1 # Would raise error - tuples are immutable
# iv.value = "new" # Would raise error - no setter defined
# To "modify", create a new IndexedValue:
iv_modified = IndexedValue([1], "new_value")
IndexedList
A specialized list container ensuring all elements are IndexedValue instances. Provides automatic conversion from various input formats and supports nested/flattened representations.
Constructor
IndexedList()
Initialize an IndexedList from an iterable.
Parameters:
items(Iterable | None): Optional iterable to convert to IndexedList- If
None, creates empty list - Can contain
IndexedValueobjects or raw values to be wrapped - Nested iterables are handled based on
nestedandflattenflags
- If
nested(bool): IfTrue, preserve nested iterables inIndexedValue.valueasIndexedListstructures. Default:False- Cannot be used with
flatten=True
- Cannot be used with
flatten(bool): IfTrue, flatten nested iterables with multi-dimensional indices. Default:False- Cannot be used with
nested=True
- Cannot be used with
Returns:
IndexedList: New indexed list instance
Raises:
ValueError: If bothnested=Trueandflatten=TrueTypeError: If items contain a mix ofIndexedValueand non-IndexedValueobjects
Examples:
# From simple values
items = IndexedList([1, 2, 3])
# Creates: [IndexedValue([0], 1), IndexedValue([1], 2), IndexedValue([2], 3)]
# From existing IndexedValue objects
iv1 = IndexedValue([0], "x")
iv2 = IndexedValue([1], "y")
items = IndexedList([iv1, iv2])
# Nested structure - preserved
nested_items = IndexedList([[1, 2], [3, 4]], nested=True)
# Creates: [
# IndexedValue([0], IndexedList([IndexedValue([0], 1), IndexedValue([1], 2)])),
# IndexedValue([1], IndexedList([IndexedValue([0], 3), IndexedValue([1], 4)]))
# ]
# Nested structure - flattened with 2D indices
flat_items = IndexedList([[1, 2], [3, 4]], flatten=True)
# Creates: [
# IndexedValue([0, 0], 1),
# IndexedValue([0, 1], 2),
# IndexedValue([1, 0], 3),
# IndexedValue([1, 1], 4)
# ]
# Empty list
empty = IndexedList()
Methods
append()
Append an IndexedValue to the list.
Parameters:
item(IndexedValue): IndexedValue to append
Raises:
TypeError: If item is not an IndexedValue
Example:
items = IndexedList()
items.append(IndexedValue([0], "value"))
# items.append("plain_value") # Would raise TypeError
extend()
Extend the list with multiple IndexedValue objects.
Parameters:
items(Iterable[IndexedValue]): Iterable of IndexedValue objects
Raises:
TypeError: If any item is not an IndexedValue
Example:
items = IndexedList()
new_items = [
IndexedValue([0], "x"),
IndexedValue([1], "y")
]
items.extend(new_items)
type()
Determine the structure type of the IndexedList.
Returns:
str: One of"normal","nested", or"flattened""normal": All IndexedValue objects have single-element indices and non-iterable values"nested": All IndexedValue objects have single-element indices and IndexedList values"flattened": All IndexedValue objects have multi-element indices
Example:
normal = IndexedList([1, 2, 3])
print(normal.type()) # "normal"
nested = IndexedList([[1, 2], [3, 4]], nested=True)
print(nested.type()) # "nested"
flat = IndexedList([[1, 2], [3, 4]], flatten=True)
print(flat.type()) # "flattened"
nested()
Convert to nested representation.
Returns:
IndexedList: New IndexedList with nested structure (type="nested")
Example:
flat = IndexedList([[1, 2], [3, 4]], flatten=True)
# [IndexedValue([0, 0], 1), IndexedValue([0, 1], 2), ...]
nested = flat.nested()
# [IndexedValue([0], IndexedList([...])), IndexedValue([1], IndexedList([...]))]
flatten()
Convert to flattened representation.
Returns:
IndexedList: New IndexedList with flattened structure (type="flattened")
Example:
nested = IndexedList([[1, 2], [3, 4]], nested=True)
flat = nested.flatten()
# [IndexedValue([0, 0], 1), IndexedValue([0, 1], 2), ...]
normal()
Convert to normal representation (single indices, simple values).
Returns:
IndexedList: New IndexedList with normal structure (type="normal")
Note:
For flattened multi-dimensional lists, this extracts the innermost values and re-indexes sequentially.
Example:
flat = IndexedList([[1, 2], [3, 4]], flatten=True)
normal = flat.normal()
# [IndexedValue([0], 1), IndexedValue([1], 2), IndexedValue([2], 3), IndexedValue([3], 4)]
__repr__()
Return detailed string representation.
Returns:
str: String representation showing all IndexedValue objects
__str__()
Return formatted string representation.
Returns:
str: Multi-line formatted string with one IndexedValue per line
Example:
items = IndexedList([1, 2, 3])
print(items)
# Output:
# IndexedList[
# [0] -> 1
# [1] -> 2
# [2] -> 3
# ]
Usage in Code Generation
CSE (Common Subexpression Elimination)
The Codegen class returns IndexedList objects containing CSE temporaries:
from jaff import Network, Codegen
net = Network("networks/react_COthin")
cg = Codegen(network=net, lang="cxx")
# Get indexed rates with CSE
result = cg.get_indexed_rates(use_cse=True, cse_var="cse")
# Access CSE temporaries (IndexedList)
cse_temps = result["extras"]["cse"]
for iv in cse_temps:
print(f"cse[{iv.indices[0]}] = {iv.value}")
# Output:
# cse[0] = sqrt(tgas)
# cse[1] = exp(-500/tgas)
# ...
# Access rate expressions (IndexedList)
rates = result["expressions"]
for iv in rates:
print(f"k[{iv.indices[0]}] = {iv.value}")
# Output:
# k[0] = 1.2e-10 * cse[0]
# k[1] = 3.4e-11 * cse[1]
# ...
Multi-Dimensional Indexing
For Jacobian matrices and other 2D structures:
# Get indexed Jacobian
result = cg.get_indexed_jacobian(use_cse=True)
# Jacobian elements (flattened)
jac_elements = result["expressions"]
for iv in jac_elements:
i, j = iv.indices # 2D indices
print(f"jac[{i}][{j}] = {iv.value}")
# Output:
# jac[0][0] = -k[0]*n[1]
# jac[0][1] = -k[0]*n[0]
# jac[1][0] = k[1]*n[2]
# ...
# Convert to nested representation
nested_jac = jac_elements.nested()
for iv in nested_jac:
row_idx = iv.indices[0]
row_elements = iv.value # IndexedList of column elements
print(f"Row {row_idx}: {len(row_elements)} elements")
Type Checking
from jaff.jaff_types import IndexedValue, IndexedList
def process_indexed_list(items: IndexedList) -> None:
"""Process an IndexedList safely."""
if not isinstance(items, IndexedList):
raise TypeError("Expected IndexedList")
for item in items:
if not isinstance(item, IndexedValue):
raise TypeError("All items must be IndexedValue")
# Process item
print(f"Index {item.indices}: {item.value}")
# Usage with Codegen results
result = cg.get_indexed_rates()
process_indexed_list(result["expressions"])
Type Conversions
Normal ↔ Nested ↔ Flattened
# Start with nested structure
data = [[1, 2], [3, 4], [5, 6]]
# Create in different formats
normal = IndexedList(data) # Default: normal (unnested)
nested = IndexedList(data, nested=True)
flat = IndexedList(data, flatten=True)
# Convert between formats
print(normal.type()) # "normal"
print(nested.type()) # "nested"
print(flat.type()) # "flattened"
# Conversion examples
flat_from_nested = nested.flatten()
normal_from_flat = flat.normal()
nested_from_flat = flat.nested()
# All conversions preserve data
assert len(flat_from_nested) == 6 # 2*3 flattened elements
Integration with Codegen Methods
Methods returning IndexedReturn:
| Method | Returns CSE | Returns Expressions |
|---|---|---|
get_indexed_rates |
IndexedList (1D) | IndexedList (1D) |
get_indexed_odes |
IndexedList (1D) | IndexedList (1D) |
get_indexed_rhs |
IndexedList (1D) | IndexedList (1D) |
get_indexed_jacobian |
IndexedList (1D) | IndexedList (2D) |
from jaff.codegen import IndexedReturn
# Type annotation for method return
def get_expressions(cg: Codegen) -> IndexedReturn:
result: IndexedReturn = cg.get_indexed_rates(use_cse=True)
# Access typed components
cse: IndexedList = result["extras"]["cse"]
exprs: IndexedList = result["expressions"]
return result
See Also
- Codegen API - Code generation methods that use these types
- File Parser API - Template parser utilizing indexed types
- Code Generation Guide - Using generated indexed expressions