Compositional Complexity in Text and Images.
The 12 matches
- [1] § MATERIALS AND METHODS › Experimental Procedure ↔ src/compositionality_study/experiment/mri/convert_hf_ds_to_local_files.py, lines 214–270 · score 0.82 · inter stimulus interval, blank trials, randomized, ISI, duration, split
- [2] § MATERIALS AND METHODS › Experimental Procedure ↔ src/compositionality_study/experiment/mri/generate_psychopy_files.py, lines 206–354 · score 0.80 · inter stimulus interval, frame rate, practice stimuli, PsychoPy, trigger, duration
- [3] § MATERIALS AND METHODS › Stimuli › Stimulus selection ↔ src/compositionality_study/data/select_stimuli.py, lines 134–208 · score 0.75 · propensity score matching, PsmPy, duplications, subset, filtered, graph
- [4] § MATERIALS AND METHODS › Image Preprocessing ↔ src/compositionality_study/models/univariate_analysis.py, lines 61–167 · score 0.72 · motion regressors, drift model, cosine, GLMs, smoothing, scans
- [5] § MATERIALS AND METHODS › Statistical Analyses ↔ src/compositionality_study/models/mvpa_decoder.py, lines 194–285 · score 0.68 · modality decoding, radius, stratified, fold, searchlight, linear
- [6] § MATERIALS AND METHODS › Stimuli › Textual compositional complexity ↔ src/compositionality_study/utils.py, lines 279–325 · score 0.61 · dependency parsing, graph depth, AMR graph, trees, edges, nodes
- [7] § MATERIALS AND METHODS › Stimuli › Textual compositional complexity ↔ src/compositionality_study/visualization/visualize_selected_stimuli.py, lines 227–307 · score 0.61 · dependency parsing, graph depth, AMR graph, attributes, trees, model
- [8] § MATERIALS AND METHODS › Image Preprocessing ↔ src/compositionality_study/models/post_hoc.py, lines 58–123 · score 0.57 · drift model, cosine, motion, GLMs, smoothing, confounds
- [9] § MATERIALS AND METHODS › Stimuli › Textual compositional complexity ↔ src/compositionality_study/utils.py, lines 279–325 · score 0.55 · dependency parsing, AMR graph, rooted, trees, edges, sentence
- [10] § MATERIALS AND METHODS › Stimuli › Visual compositional complexity ↔ src/compositionality_study/utils.py, lines 227–257 · score 0.53 · graph depth, AMR graphs, longest, shortest, edges, nodes
- [11] § RESULTS › Multivariate Pattern Analysis of Compositional Complexity ↔ src/compositionality_study/models/estimate_betas.py, lines 82–128 · score 0.51 · extra confounds, aspect ratio, beta, nodes, model, AMR
- [12] § RESULTS › Univariate Analysis of Compositional Complexity ↔ src/compositionality_study/models/univariate_analysis.py, lines 536–594 · score 0.50 · functional localization mask, conjunction, row, confounds, frame, Univariate
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 510 lines · 18 KB · MIT · 3 matches
- """Utils for the compositionality project."""
- from typing import Dict, Iterable, List, Optional, Tuple, Union
- from pathlib import Path
- import amrlib
- import networkx as nx
- import nibabel as nib
- import numpy as np
- import pandas as pd
- import penman
- from datasets import load_dataset
- from loguru import logger
- from nilearn import image, plotting
- from nilearn.glm.thresholding import threshold_stats_img
- from penman.exceptions import DecodeError
- from PIL import Image, ImageOps
- from spacy.language import Language # type: ignore
- from spacy.tokens import Doc, Token
- from compositionality_study.constants import HF_DATASET_NAME
- # Set up the spacy amrlib extension
- amrlib.setup_spacy_extension()
- _COCO_DF = None
- DEFAULT_VOXEL_P = 0.001
- DEFAULT_CLUSTER_THRESHOLD = 50
- def get_coco_df() -> pd.DataFrame:
- """Load and cache the COCO dataset."""
- global _COCO_DF
- if _COCO_DF is None:
- ds = load_dataset(HF_DATASET_NAME, split="train")
- df = ds.to_pandas()
- if isinstance(df, pd.DataFrame):
- _COCO_DF = df
- else:
- raise ValueError("Expected DataFrame")
- return _COCO_DF # type: ignore
- def get_stimulus_features_lookup(coco_df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame]:
- """Create optimized lookup tables for text and image features."""
- txt_df = (
- coco_df.drop_duplicates("sentences_raw").set_index("sentences_raw")
- if "sentences_raw" in coco_df.columns
- else pd.DataFrame()
- )
- img_df = (
- coco_df.drop_duplicates("cocoid").set_index("cocoid")
- if "cocoid" in coco_df.columns
- else pd.DataFrame()
- )
- return txt_df, img_df
- def get_stimulus_data(
- modality: Optional[str],
- stimulus: Optional[str],
- cocoid: Union[str, float, int, None],
- txt_df: pd.DataFrame,
- img_df: pd.DataFrame,
- ) -> Union[pd.Series, None]: # type: ignore
- """Retrieve features for a single stimulus event."""
- if modality == "text" and stimulus in txt_df.index:
- res = txt_df.loc[stimulus]
- return res if isinstance(res, pd.Series) else res.iloc[0]
- elif modality == "image":
- if pd.notna(cocoid):
- if cocoid in img_df.index:
- res = img_df.loc[cocoid]
- return res if isinstance(res, pd.Series) else res.iloc[0]
- try:
- # Handle potential float/string mismatches
- int_cid = int(cocoid) # type: ignore
- if int_cid in img_df.index:
- res = img_df.loc[int_cid]
- return res if isinstance(res, pd.Series) else res.iloc[0]
- except (ValueError, TypeError):
- pass
- return None
- def derive_run_id(bold_file: Path) -> str:
- """Strip space/desc tokens to get the BIDS run identifier."""
- stem = bold_file.name.replace(".nii.gz", "").replace(".nii", "")
- parts: List[str] = []
- for token in stem.split("_"):
- if token.startswith("space-") or token.startswith("desc-"):
- break
- parts.append(token)
- return "_".join(parts)
- def load_events(events_tsv: Path) -> pd.DataFrame:
- """Load events and normalize the condition column name."""
- events = pd.read_csv(events_tsv, sep="\t")
- modality = events["modality"].astype(str).str.strip().str.lower()
- complexity = events["complexity"].astype(str).str.strip().str.lower()
- events["trial_type"] = modality + "_" + complexity
- if "trial_type" in events.columns:
- events = events.dropna(subset=["trial_type"])
- events["trial_type"] = events["trial_type"].astype(str)
- return events
- def find_gm_mask(fmriprep_dir: Path, subject: str) -> Path:
- """Pick the GM probseg (assuming MNI152NLin2009cAsym space)."""
- anat_dir = fmriprep_dir / f"sub-{subject}" / "ses-01" / "anat"
- candidates = sorted(anat_dir.glob("*space-MNI152NLin2009cAsym*_label-GM_probseg.nii.gz"))
- return candidates[0]
- def collect_runs(
- fmriprep_dir: Path, bids_dir: Path, subject: str, max_runs: int | None = None
- ) -> List[Tuple[Path, Path, Path]]:
- """Pair each preprocessed BOLD run with matching events and confounds."""
- bold_files = sorted((fmriprep_dir / f"sub-{subject}").glob("**/*_desc-preproc_bold.nii.gz"))
- runs: List[Tuple[Path, Path, Path]] = []
- if max_runs is not None:
- logger.info(f"Debugging: limiting to {max_runs} runs for subject {subject}")
- bold_files = bold_files[:max_runs]
- for bold_file in bold_files:
- run_id = derive_run_id(bold_file)
- rel_dir = bold_file.parent.relative_to(fmriprep_dir / f"sub-{subject}")
- events_file = bids_dir / f"sub-{subject}" / rel_dir / f"{run_id}_events.tsv"
- confounds_file = bold_file.parent / f"{run_id}_desc-confounds_timeseries.tsv"
- runs.append((bold_file, events_file, confounds_file))
- return runs
- def load_stimulus_confounds(events: pd.DataFrame, n_scans: int, tr: float) -> pd.DataFrame:
- """Generate extra confounds based on stimulus properties."""
- coco_df = get_coco_df()
- text_cols = ["sentence_length", "amr_n_nodes"]
- img_cols = ["coco_a_nodes", "ic_score", "aspect_ratio", "coco_person"]
- all_cols = text_cols + img_cols
- confounds = pd.DataFrame(0.0, index=range(n_scans), columns=all_cols)
- txt_df, img_df = get_stimulus_features_lookup(coco_df)
- for _, row in events.iterrows():
- data = get_stimulus_data(
- modality=row.get("modality"),
- stimulus=row.get("stimulus"),
- cocoid=row.get("cocoid"),
- txt_df=txt_df,
- img_df=img_df,
- )
- if data is None:
- continue
- start_tr = int(round(row["onset"] / tr))
- n_trs = int(round(row["duration"] / tr))
- end_tr = min(start_tr + n_trs, n_scans)
- if start_tr >= n_scans:
- continue
- cols = text_cols if row.get("modality") == "text" else img_cols
- valid_cols = [c for c in cols if c in data]
- if valid_cols:
- confounds.iloc[
- start_tr:end_tr, confounds.columns.get_indexer(valid_cols) # type: ignore
- ] = data[valid_cols].values
- return confounds
- def conjunction_map(map_a: Path, map_b: Path) -> Path:
- """Compute a simple conjunction (logical AND) of two thresholded maps."""
- img_a = image.math_img("img != 0", img=map_a)
- img_b = image.math_img("img != 0", img=map_b)
- conj = image.math_img("img1 * img2", img1=img_a, img2=img_b)
- out_file = map_a.with_name("conjunction_img_txt.nii.gz")
- save_brain_map(conj, out_file)
- return out_file
- def split_runs_by_session(runs: Iterable[Tuple[Path, Path, Path]]) -> Dict[str, List[Tuple[Path, Path, Path]]]:
- """Group runs by session token for downstream localizer logic."""
- grouped: Dict[str, List[Tuple[Path, Path, Path]]] = {}
- for bold, events, confounds in runs:
- run_id = derive_run_id(bold)
- session = run_id.split("_")[1]
- grouped.setdefault(session, []).append((bold, events, confounds))
- return grouped
- def get_aspect_ratio(filepath: str):
- """Get the aspect ratio of the image from the local directory.
- :param filepath: The path to the image file
- :type filepath: str
- :return: The aspect ratio of the image
- :rtype: float
- """
- # Load the image
- try:
- with Image.open(filepath) as img:
- width, height = img.size
- return width / height
- except Exception as e: # noqa
- return 0.0
- def get_amr_graph_depth(
- amr_graph: str,
- return_graph=False,
- ) -> Union[int, Tuple[int, nx.DiGraph]]:
- """Get the depth of the AMR graph for a given example.
- :param amr_graph: The AMR graph to get the depth for (output of a spacy doc._.to_amr()[0] call)
- :type amr_graph: str
- :param return_graph: Whether to return the networkx graph, defaults to False
- :type return_graph: bool, optional
- :return: The maximum "depth" of the AMR graph (longest shortest path)
- :rtype: int
- """
- # Convert to a Penman graph (with de-inverted edges)
- penman_graph = penman.decode(amr_graph)
- # Convert to a nx graph, first initialize the nx graph
- nx_graph = nx.DiGraph()
- # Add edges
- for e in penman_graph.edges():
- nx_graph.add_edge(e.source, e.target)
- # Get the characteristic path length of the graph
- amr_graph_depth = (
- max([max(nx.shortest_path_length(nx_graph, source=n).values()) for n in nx_graph.nodes()])
- if nx.number_of_nodes(nx_graph) > 0
- else 0
- )
- if return_graph:
- return amr_graph_depth, nx_graph
- else:
- return amr_graph_depth
- def walk_tree(
- node: Token,
- depth: int,
- ) -> int:
- """Walk the dependency parse tree and return the maximum depth.
- :param node: The current node in the tree
- :type node: spacy.tokens.Token
- :param depth: The current depth in the tree
- :type depth: int
- :return: The maximum depth in the tree
- :rtype: int
- """
- if node.n_lefts + node.n_rights > 0:
- return max(walk_tree(child, depth + 1) for child in node.children)
- else:
- return depth
- def derive_text_depth_features(
- examples: Dict[str, List],
- nlp: Language,
- ) -> Dict[str, List]:
- """Get the depth of the dep parse tree, number of verbs and "depth" of the AMR graph of an example caption.
- The AMR model needs to be downloaded separately, see https://github.com/bjascob/amrlib-models.
- :param examples: A batch of hf dataset examples
- :type examples: Dict[str, List]
- :param nlp: Spacy pipeline to use, initialized using nlp = spacy.load("en_core_web_trf")
- :type nlp: spacy.language.Language
- :return: The batch with the added features
- :rtype: Dict[str, List]
- """
- result: Dict = {
- "parse_tree_depth": [],
- "n_verbs": [],
- "amr_graph_depth": [],
- "amr_graph": [],
- "amr_n_nodes": [],
- "amr_n_edges": [],
- }
- doc_batched = nlp.pipe(examples["sentences_raw"])
- for doc in doc_batched:
- # Also derive the AMR graph for the caption and derive its depth
- amr_graph = doc._.to_amr()[0] # type: ignore
- try:
- amr_depth, amr_graph_obj = get_amr_graph_depth(amr_graph, return_graph=True) # type: ignore
- n_nodes = nx.number_of_nodes(amr_graph_obj)
- n_edges = nx.number_of_edges(amr_graph_obj)
- amr_graph_arr = nx.to_numpy_array(amr_graph_obj)
- except DecodeError:
- amr_depth = 0
- n_nodes = 0
- n_edges = 0
- amr_graph_arr = nx.to_numpy_array(nx.DiGraph())
- # Determine the depth of the dependency parse tree
- result["parse_tree_depth"].append(walk_tree(next(doc.sents).root, 0))
- result["n_verbs"].append(len([token for token in doc if token.pos_ == "VERB"]))
- result["amr_graph_depth"].append(amr_depth)
- result["amr_graph"].append(amr_graph_arr)
- result["amr_n_nodes"].append(n_nodes)
- result["amr_n_edges"].append(n_edges)
- return examples | result
- def dependency_parse_to_nx(
- sents: List[Doc],
- ):
- """Convert spaCy sentence objects into a NetworkX directed graph representing the dependency parse tree.
- :param sents: A list of spaCy sentence objects
- :type sents: List[spacy.tokens.Doc]
- :return: A NetworkX directed graph representing the dependency parse tree
- :rtype: nx.DiGraph
- """
- # Create a directed graph
- graph = nx.DiGraph()
- # Iterate over sentences
- for sent in sents:
- for token in sent:
- # Add node for the token with attributes
- graph.add_node(token.i, text=token.text, pos=token.pos_, tag=token.tag_)
- # Add edge from head to child (if not the root token)
- if token.head != token: # Avoid self-loop for the root
- graph.add_edge(token.head.i, token.i, dep=token.dep_)
- return graph
- def flatten_examples(
- examples: Dict[str, List],
- flatten_col_names: List[str] = ["sentences_raw", "sentids"],
- ) -> Dict[str, List]:
- """Flattens the examples in the dataset.
- :param examples: The examples to flatten
- :type examples: Dict[str, List]
- :param flatten_col_names: The column names to flatten, defaults to ["sentences_raw", "sentids"]
- :type flatten_col_names: List[str]
- :return: The flattened examples
- :rtype: Dict[str, List]
- """
- flattened_data = {}
- number_of_sentences = [len(sents) for sents in examples[flatten_col_names[0]]]
- for key, value in examples.items():
- if key in flatten_col_names:
- flattened_data[key] = [sent for sent_list in value for sent in sent_list]
- else:
- flattened_data[key] = np.repeat(value, number_of_sentences).tolist()
- return flattened_data
- def apply_gamma_correction(
- image: Image.Image,
- target_mean=128.0,
- ) -> Image.Image:
- """Apply gamma correction to an image.
- :param image: The image to apply gamma correction to
- :type image: PIL.Image.Image
- :param target_mean: The target mean brightness of the image, defaults to 128.0
- :type target_mean: float, optional
- :return: The image with gamma correction applied
- :rtype: PIL.Image.Image
- """
- # Convert the PIL image to a numpy array
- img_array = np.array(image)
- # Calculate the current mean brightness of the image
- current_mean = np.mean(img_array)
- # Calculate the gamma value to adjust the mean to the target mean
- # Avoid division by zero
- if current_mean > 0:
- gamma = np.log(target_mean) / np.log(current_mean)
- # Apply gamma correction to the image
- corrected_image = ImageOps.autocontrast(image, cutoff=gamma) # type: ignore
- return corrected_image
- else:
- return image
- def save_brain_map(
- img: Union[nib.nifti1.Nifti1Image, object],
- output_path: Union[str, Path],
- is_z_map: bool = False,
- voxel_p: float = DEFAULT_VOXEL_P,
- cluster_threshold: int = DEFAULT_CLUSTER_THRESHOLD,
- make_surface_plot: bool = True,
- surface_plot_kwargs: Optional[Dict] = None,
- sided: str = "positive",
- ) -> Optional[nib.nifti1.Nifti1Image]:
- """Save a brain map to disk, generate mosaic plots, and cluster tables.
- For z-score/stat maps (``is_z_map=True``), apply one-sided FPR thresholding
- with ``threshold_stats_img`` (using ``voxel_p`` and ``cluster_threshold``).
- Set ``sided="positive"`` to keep only positive effects, ``sided="negative"``
- for negative effects, or ``sided="both"`` to save both directions (files
- suffixed with ``_pos_thr`` and ``_neg_thr``).
- :param img: The brain map image
- :param output_path: Where to save the image
- :param is_z_map: Whether the image is a z-/stat-map that should be thresholded
- :param voxel_p: Two-sided voxel-wise p-value used for thresholding
- :param make_surface_plot: Whether to save a surface plot
- :param surface_plot_kwargs: Extra args for ``generate_surface_plots``
- :param sided: Direction for one-sided thresholding (positive, negative, both)
- :returns: Thresholded positive image if available, otherwise None
- """
- output_path = Path(output_path)
- output_path.parent.mkdir(parents=True, exist_ok=True)
- def _base_path(path: Path) -> str:
- str_path = str(path)
- if str_path.endswith(".nii.gz"):
- return str_path[:-7]
- if str_path.endswith(".nii"):
- return str_path[:-4]
- return str(path.with_suffix(""))
- def _plot(target_img: object, target_path: Path, cmap: Optional[str] = None) -> None:
- base = _base_path(target_path)
- try:
- display = plotting.plot_stat_map(
- target_img,
- display_mode="mosaic",
- cut_coords=5,
- cmap=cmap, # type: ignore
- )
- display.savefig(f"{base}_mosaic.png") # type: ignore
- display.close() # type: ignore
- except Exception as e:
- logger.error(f"Failed to plot brain map: {e}")
- # Save the Nifti image
- if hasattr(img, "to_filename"):
- img.to_filename(output_path) # type: ignore
- else:
- nib.save(img, output_path) # type: ignore
- _plot(img, output_path)
- thresholded_img = None
- if is_z_map:
- try:
- do_pos = sided in ("positive", "both")
- do_neg = sided in ("negative", "both")
- def _save_thr(target_img: object, suffix: str, flip_back: bool = False, cmap: Optional[str] = None) -> object:
- thr_img, _ = threshold_stats_img(
- target_img,
- alpha=voxel_p,
- height_control="fpr",
- cluster_threshold=cluster_threshold,
- two_sided=False,
- )
- if flip_back:
- thr_img = image.math_img("-img", img=thr_img)
- if str(output_path).endswith(".nii.gz"):
- thr_path = output_path.with_name(output_path.name.replace(".nii.gz", f"_{suffix}.nii.gz"))
- else:
- thr_path = output_path.with_name(f"{output_path.stem}_{suffix}{output_path.suffix}")
- thr_img.to_filename(thr_path) # type: ignore
- _plot(thr_img, thr_path, cmap=cmap)
- return thr_img
- if do_pos:
- thresholded_img = _save_thr(img, "pos_thr", cmap="Reds")
- if do_neg:
- _save_thr(image.math_img("-img", img=img), "neg_thr", flip_back=True, cmap="Blues_r")
- except Exception as e:
- logger.error(f"Failed to threshold brain map: {e}")
- # Save surface plot
- if make_surface_plot:
- from compositionality_study.visualization.visualize_brain_maps import generate_surface_plots
- surface_plot_kwargs = surface_plot_kwargs or {}
- try:
- generate_surface_plots(
- nii_file=output_path,
- output_dir=output_path.parent,
- **surface_plot_kwargs
- )
- except Exception as e:
- logger.error(f"Failed to generate surface plot: {e}")
- return thresholded_img # type: ignore
utils.py at commit bb03182, under MIT · at the source
Overview
- Laboratory for Cognitive Neurology, Department of Neurosciences, Leuven Brain Institute, KU Leuven, Leuven, Belgium
- Language Intelligence and Information Retrieval Lab, Department of Computer Science, KU Leuven, Leuven, Belgium
Abstract
Compositionality enables us to derive the meaning of a complex whole from the syntax and semantics of its individual parts. While compositional processing has primarily been studied in language, similar principles apply to visual scenes, in which meaning can be derived from their individual parts and the relationships between them. In this study, we therefore aimed to explore whether there is a shared neural basis for compositional processing in text and images. Based on abstract meaning representation graphs for text, commonly used to capture “who is doing what to whom”, and action graph annotations for visual scenes, we defined an analogous graph depth-based notion of compositional complexity. By conducting a functional magnetic resonance imaging experiment in which participants view text-image pairs from the Common Objects in Context-Actions data set, we aimed to identify brain activity patterns related to compositional processing through a combination of univariate and multivariate pattern analysis. Specifically, we hypothesized that there are shared brain regions—such as the pars opercularis, pars triangularis or anterior temporal lobe—involved in processing compositional complexity across text and images. Alternatively, there might exist distinct regions for processing compositional complexity in text and images, without any substantial cross-modal overlap. Our results provided no evidence in support of either hypothesis: no significant neural responses to compositional complexity were observed, in either a shared or modality-specific manner. Given the rigorous control of confounds in our study design, the operationalization of compositional complexity itself may underlie the null result.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above, with 12 matches between paragraphs and lines of code.
lcn-kul/compositionality-study
bb03182371decdfede04bfac39e5dc335e2a789d, 22 April 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
33 files
- docs/
source/ , Python, 242 linesconf.py - setup.py, Python, 8 lines
- src/
compositionality_study/ , Python, 1 line__init__.py - src/
compositionality_study/ , Python, 96 linesconstants.py - src/
compositionality_study/ , Python, 1 linedata/ __init__.py - src/
compositionality_study/ , Python, 234 linesdata/ characterize_img_comp_ic 9600.py - src/
compositionality_study/ , Python, 97 linesdata/ download_coco.py - src/
compositionality_study/ , Python, 645 linesdata/ preprocess_coco.py - src/
compositionality_study/ , Python, 291 lines, 1 matchdata/ select_stimuli.py - src/
compositionality_study/ , Python, 1 lineeda/ __init__.py - src/
compositionality_study/ , Python, 281 lineseda/ explore_statistics.py - src/
compositionality_study/ , Python, 1 lineexperiment/ __init__.py - src/
compositionality_study/ , Python, 1 lineexperiment/ behavioral/ __init__.py - src/
compositionality_study/ , Python, 473 linesexperiment/ behavioral/ evaluate_memory_test.py - src/
compositionality_study/ , Python, 248 linesexperiment/ behavioral/ memory_test.py - src/
compositionality_study/ , Python, 1 lineexperiment/ mri/ __init__.py - src/
compositionality_study/ , Python, 71 linesexperiment/ mri/ check_mriqc.py - src/
compositionality_study/ , Python, 520 lines, 1 matchexperiment/ mri/ convert_hf_ds_to_local_f iles.py - src/
compositionality_study/ , Python, 66 linesexperiment/ mri/ create_fourier_scrambled _images.py - src/
compositionality_study/ , Python, 364 lines, 1 matchexperiment/ mri/ generate_psychopy_files. py - src/
compositionality_study/ , Python, 1 linemodels/ __init__.py - src/
compositionality_study/ , Python, 315 lines, 1 matchmodels/ estimate_betas.py - src/
compositionality_study/ , Python, 177 linesmodels/ icnet.py - src/
compositionality_study/ , Python, 470 lines, 1 matchmodels/ mvpa_decoder.py - src/
compositionality_study/ , Python, 359 lines, 1 matchmodels/ post_hoc.py - src/
compositionality_study/ , Python, 598 lines, 2 matchesmodels/ univariate_analysis.py - src/
compositionality_study/ , Python, 510 lines, 3 matchesutils.py - src/
compositionality_study/ , Python, 1 linevisualization/ __init__.py - src/
compositionality_study/ , Python, 254 linesvisualization/ visualize_brain_maps.py - src/
compositionality_study/ , Python, 618 lines, 1 matchvisualization/ visualize_selected_stimu li.py - src/
compositionality_study/ , Python, 81 linesvisualization/ visualize_stimuli_catego ries.py - LICENSE, License, 21 lines
- README.md, Text, 27 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 31 scripts, each with its path and the digest of its content;
- 12 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
Datasets cited
- osf:ta45d, at OSF; found in “DATA AND CODE AVAILABILITY STATEMENTS”
- zenodo:18831683, at Zenodo; found in “DATA AND CODE AVAILABILITY STATEMENTS”
- zenodo:18832298, at Zenodo; found in “DATA AND CODE AVAILABILITY STATEMENTS”
Data and code availability statements
The approved Stage 1 protocol, laboratory log, stimuli and behavioral results can be found on the Open Science Framework: https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 2, 28 September 2026
- Funding: added Fonds Wetenschappelijk Onderzoek: 10.13039/501100003130, 1154623N, 501100003130, 1247821N; KU Leuven: 501100004040, C14/21/109
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, pages, dates, 6 authors, 3 keywords, 71 references.
Cite
This paper
Balabin, H., Liuzzi, A. G., Statz, K., Moens, M.-F., Dupont, P., & Vandenberghe, R. (2026). Compositional Complexity in Text and Images. Neurobiology of language (Cambridge, Mass.), 7, NOL.a.271. https://
BibTeX
@article{balabin2026comp
author = {Balabin, Helena and Liuzzi, Antonietta Gabriella and Statz, Kevin and Moens, Marie-Francine and Dupont, Patrick and Vandenberghe, Rik},
title = {{Compositional Complexity in Text and Images}},
journal = {Neurobiology of language (Cambridge, Mass.)},
year = {2026},
month = jul,
volume = {7},
pages = {NOL.a.271},
publisher = {MIT Press},
issn = {2641-4368},
doi = {10.1162/
url = {https://
pmid = {42644209},
pmcid = {PMC13506228}
}
RIS
TY - JOUR
AU - Balabin, Helena
AU - Liuzzi, Antonietta Gabriella
AU - Statz, Kevin
AU - Moens, Marie-Francine
AU - Dupont, Patrick
AU - Vandenberghe, Rik
TI - Compositional Complexity in Text and Images
T2 - Neurobiology of language (Cambridge, Mass.)
J2 - Neurobiol Lang (Camb)
PY - 2026
DA - 2026/
VL - 7
SP - NOL.a.271
SN - 2641-4368
PB - MIT Press
DO - 10.1162/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1162/
"type": "article-journal",
"title": "Compositional Complexity in Text and Images",
"container-title": "Neurobiology of language (Cambridge, Mass.)",
"author": [
{
"family": "Balabin",
"given": "Helena"
},
{
"family": "Liuzzi",
"given": "Antonietta Gabriella"
},
{
"family": "Statz",
"given": "Kevin"
},
{
"family": "Moens",
"given": "Marie-Francine"
},
{
"family": "Dupont",
"given": "Patrick"
},
{
"family": "Vandenberghe",
"given": "Rik"
}
],
"container-title-short":
"volume": "7",
"page": "NOL.a.271",
"DOI": "10.1162/
"PMID": "42644209",
"PMCID": "PMC13506228",
"ISSN": "2641-4368",
"publisher": "MIT Press",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
9
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.7554/elife.107933 [code]
- Modality-agnostic decoding of vision and language from fMRI.Journal: eLifeIn common: Nilearn, OpenCV, Pillow, 8 other tools, fMRI, 7 references
- [2] doi:10.1038/s41597-026-07248-6 [code]
- A large-scale fMRI dataset for vision-language semantic association.Journal: Scientific dataIn common: GLMsingle, Nilearn, OpenCV, 8 other tools, fMRI, 4 references
- [3] doi:10.1523/jneurosci.0038-26.2026 [code]
- Multidimensional Feature Tuning in Category Selective Areas of Human Visual Cortex.Journal: The Journal of neuroscience : the official journal of the Society for NeuroscienceIn common: Pillow, NiBabel, PyTorch, 6 other tools, fMRI, 6 references
- [4] doi:10.1162/imag.a.1309 [code]
- Probing the content of semantic representations in body-selective regions.Journal: Imaging neuroscience (Cambridge, Mass.)In common: OpenCV, Pillow, NiBabel, 7 other tools, 5 references
- [5] doi:10.1038/s41467-026-76098-y [code]
- A single computational objective can produce specialization of streams in visual cortex.Journal: Nature communicationsIn common: Pillow, NiBabel, PyTorch, 6 other tools, 5 references
- [6] doi:10.1038/s42003-026-10169-0 [code]
- Shared representations in brains and models reveal a two-route cortical organization during scene perception.Journal: Communications biologyIn common: Pillow, NiBabel, PyTorch, 6 other tools, 5 references
- [7] doi:10.1162/imag.a.1286 [code]
- Behavioral imitation with artificial neural networks leads to personalized models of brain dynamics during videogame play.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Nilearn, Pillow, NiBabel, 7 other tools, fMRI, 4 references
- [8] doi:10.1162/imag.a.1256 [code]
- Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Nilearn, Pillow, NiBabel, 7 other tools, fMRI, 4 references
- [9] doi:10.1002/hbm.70469 [code]
- VarCoNet: A Variability-Aware Self-Supervised Framework for Functional Connectome Extraction From Resting-State fMRI.Journal: Human brain mappingIn common: Nilearn, NetworkX, OpenCV, 9 other tools, fMRI, 1 reference
- [10] doi:10.1038/s41597-026-06869-1 [code]
- Individual Brain Charting: fifth release of high-resolution fMRI data for cognitive mapping.Journal: Scientific dataIn common: PsychoPy, Nilearn, Pillow, 7 other tools, fMRI, 2 references
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 31 scripts, and 12 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:6da6966370ae243d…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
