Files
copycat/slipnet_analysis/export_slipnet.py
Alex Linhares 50b6fbdc27 Add slipnet analysis: depth vs topology correlation study
Analysis shows no significant correlation between conceptual depth
and hop distance to letter nodes (r=0.281, p=0.113). Includes
Python scripts, visualizations, and LaTeX paper.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 20:58:15 +00:00

335 lines
13 KiB
Python

#!/usr/bin/env python3
"""Export the Slipnet structure to a JSON file.
This script is self-contained to avoid Python version compatibility issues
with the main copycat package.
"""
import json
class Slipnode:
"""Minimal Slipnode for export purposes."""
def __init__(self, slipnet, name, depth, length=0.0):
self.slipnet = slipnet
self.name = name
self.conceptualDepth = depth
self.intrinsicLinkLength = length
self.shrunkLinkLength = length * 0.4
self.codelets = []
self.categoryLinks = []
self.instanceLinks = []
self.propertyLinks = []
self.lateralSlipLinks = []
self.lateralNonSlipLinks = []
self.incomingLinks = []
self.outgoingLinks = []
class Sliplink:
"""Minimal Sliplink for export purposes."""
def __init__(self, source, destination, label=None, length=0.0):
self.source = source
self.destination = destination
self.label = label
self.fixedLength = length
source.outgoingLinks.append(self)
destination.incomingLinks.append(self)
class SlipnetExporter:
"""Recreates the Slipnet structure for export."""
def __init__(self):
self.slipnodes = []
self.sliplinks = []
self._addInitialNodes()
self._addInitialLinks()
def _addNode(self, name, depth, length=0):
slipnode = Slipnode(self, name, depth, length)
self.slipnodes.append(slipnode)
return slipnode
def _addLink(self, source, destination, label=None, length=0.0):
link = Sliplink(source, destination, label=label, length=length)
self.sliplinks.append(link)
return link
def _addSlipLink(self, source, destination, label=None, length=0.0):
link = self._addLink(source, destination, label, length)
source.lateralSlipLinks.append(link)
def _addNonSlipLink(self, source, destination, label=None, length=0.0):
link = self._addLink(source, destination, label, length)
source.lateralNonSlipLinks.append(link)
def _addBidirectionalLink(self, source, destination, length):
self._addNonSlipLink(source, destination, length=length)
self._addNonSlipLink(destination, source, length=length)
def _addCategoryLink(self, source, destination, length):
link = self._addLink(source, destination, None, length)
source.categoryLinks.append(link)
def _addInstanceLink(self, source, destination, length=100.0):
categoryLength = source.conceptualDepth - destination.conceptualDepth
self._addCategoryLink(destination, source, categoryLength)
link = self._addLink(source, destination, None, length)
source.instanceLinks.append(link)
def _addPropertyLink(self, source, destination, length):
link = self._addLink(source, destination, None, length)
source.propertyLinks.append(link)
def _addOppositeLink(self, source, destination):
self._addSlipLink(source, destination, label=self.opposite)
self._addSlipLink(destination, source, label=self.opposite)
def _link_items_to_their_neighbors(self, items):
previous = items[0]
for item in items[1:]:
self._addNonSlipLink(previous, item, label=self.successor)
self._addNonSlipLink(item, previous, label=self.predecessor)
previous = item
def _addInitialNodes(self):
self.letters = []
for c in 'abcdefghijklmnopqrstuvwxyz':
slipnode = self._addNode(c, 10.0)
self.letters.append(slipnode)
self.numbers = []
for c in '12345':
slipnode = self._addNode(c, 30.0)
self.numbers.append(slipnode)
# string positions
self.leftmost = self._addNode('leftmost', 40.0)
self.rightmost = self._addNode('rightmost', 40.0)
self.middle = self._addNode('middle', 40.0)
self.single = self._addNode('single', 40.0)
self.whole = self._addNode('whole', 40.0)
# alphabetic positions
self.first = self._addNode('first', 60.0)
self.last = self._addNode('last', 60.0)
# directions
self.left = self._addNode('left', 40.0)
self.left.codelets += ['top-down-bond-scout--direction']
self.left.codelets += ['top-down-group-scout--direction']
self.right = self._addNode('right', 40.0)
self.right.codelets += ['top-down-bond-scout--direction']
self.right.codelets += ['top-down-group-scout--direction']
# bond types
self.predecessor = self._addNode('predecessor', 50.0, 60.0)
self.predecessor.codelets += ['top-down-bond-scout--category']
self.successor = self._addNode('successor', 50.0, 60.0)
self.successor.codelets += ['top-down-bond-scout--category']
self.sameness = self._addNode('sameness', 80.0)
self.sameness.codelets += ['top-down-bond-scout--category']
# group types
self.predecessorGroup = self._addNode('predecessorGroup', 50.0)
self.predecessorGroup.codelets += ['top-down-group-scout--category']
self.successorGroup = self._addNode('successorGroup', 50.0)
self.successorGroup.codelets += ['top-down-group-scout--category']
self.samenessGroup = self._addNode('samenessGroup', 80.0)
self.samenessGroup.codelets += ['top-down-group-scout--category']
# other relations
self.identity = self._addNode('identity', 90.0)
self.opposite = self._addNode('opposite', 90.0, 80.0)
# objects
self.letter = self._addNode('letter', 20.0)
self.group = self._addNode('group', 80.0)
# categories
self.letterCategory = self._addNode('letterCategory', 30.0)
self.stringPositionCategory = self._addNode('stringPositionCategory', 70.0)
self.stringPositionCategory.codelets += ['top-down-description-scout']
self.alphabeticPositionCategory = self._addNode('alphabeticPositionCategory', 80.0)
self.alphabeticPositionCategory.codelets += ['top-down-description-scout']
self.directionCategory = self._addNode('directionCategory', 70.0)
self.bondCategory = self._addNode('bondCategory', 80.0)
self.groupCategory = self._addNode('groupCategory', 80.0)
self.length = self._addNode('length', 60.0)
self.objectCategory = self._addNode('objectCategory', 90.0)
self.bondFacet = self._addNode('bondFacet', 90.0)
self.initiallyClampedSlipnodes = [
self.letterCategory,
self.stringPositionCategory,
]
def _addInitialLinks(self):
self._link_items_to_their_neighbors(self.letters)
self._link_items_to_their_neighbors(self.numbers)
# letter categories
for letter in self.letters:
self._addInstanceLink(self.letterCategory, letter, 97.0)
self._addCategoryLink(self.samenessGroup, self.letterCategory, 50.0)
# lengths
for number in self.numbers:
self._addInstanceLink(self.length, number)
groups = [self.predecessorGroup, self.successorGroup, self.samenessGroup]
for group in groups:
self._addNonSlipLink(group, self.length, length=95.0)
opposites = [
(self.first, self.last),
(self.leftmost, self.rightmost),
(self.left, self.right),
(self.successor, self.predecessor),
(self.successorGroup, self.predecessorGroup),
]
for a, b in opposites:
self._addOppositeLink(a, b)
# properties
self._addPropertyLink(self.letters[0], self.first, 75.0)
self._addPropertyLink(self.letters[-1], self.last, 75.0)
links = [
# object categories
(self.objectCategory, self.letter),
(self.objectCategory, self.group),
# string positions
(self.stringPositionCategory, self.leftmost),
(self.stringPositionCategory, self.rightmost),
(self.stringPositionCategory, self.middle),
(self.stringPositionCategory, self.single),
(self.stringPositionCategory, self.whole),
# alphabetic positions
(self.alphabeticPositionCategory, self.first),
(self.alphabeticPositionCategory, self.last),
# direction categories
(self.directionCategory, self.left),
(self.directionCategory, self.right),
# bond categories
(self.bondCategory, self.predecessor),
(self.bondCategory, self.successor),
(self.bondCategory, self.sameness),
# group categories
(self.groupCategory, self.predecessorGroup),
(self.groupCategory, self.successorGroup),
(self.groupCategory, self.samenessGroup),
# bond facets
(self.bondFacet, self.letterCategory),
(self.bondFacet, self.length),
]
for a, b in links:
self._addInstanceLink(a, b)
# link bonds to their groups
self._addNonSlipLink(self.sameness, self.samenessGroup, label=self.groupCategory, length=30.0)
self._addNonSlipLink(self.successor, self.successorGroup, label=self.groupCategory, length=60.0)
self._addNonSlipLink(self.predecessor, self.predecessorGroup, label=self.groupCategory, length=60.0)
# link bond groups to their bonds
self._addNonSlipLink(self.samenessGroup, self.sameness, label=self.bondCategory, length=90.0)
self._addNonSlipLink(self.successorGroup, self.successor, label=self.bondCategory, length=90.0)
self._addNonSlipLink(self.predecessorGroup, self.predecessor, label=self.bondCategory, length=90.0)
# letter category to length
self._addSlipLink(self.letterCategory, self.length, length=95.0)
self._addSlipLink(self.length, self.letterCategory, length=95.0)
# letter to group
self._addSlipLink(self.letter, self.group, length=90.0)
self._addSlipLink(self.group, self.letter, length=90.0)
# direction-position, direction-neighbor, position-neighbor
self._addBidirectionalLink(self.left, self.leftmost, 90.0)
self._addBidirectionalLink(self.right, self.rightmost, 90.0)
self._addBidirectionalLink(self.right, self.leftmost, 100.0)
self._addBidirectionalLink(self.left, self.rightmost, 100.0)
self._addBidirectionalLink(self.leftmost, self.first, 100.0)
self._addBidirectionalLink(self.rightmost, self.first, 100.0)
self._addBidirectionalLink(self.leftmost, self.last, 100.0)
self._addBidirectionalLink(self.rightmost, self.last, 100.0)
# other
self._addSlipLink(self.single, self.whole, length=90.0)
self._addSlipLink(self.whole, self.single, length=90.0)
def export_slipnet():
"""Export slipnet nodes and links to a JSON structure."""
slipnet = SlipnetExporter()
# Build node data
nodes = []
node_to_id = {}
for node in slipnet.slipnodes:
node_to_id[node] = node.name
node_data = {
"name": node.name,
"conceptualDepth": node.conceptualDepth,
"intrinsicLinkLength": node.intrinsicLinkLength,
"shrunkLinkLength": node.shrunkLinkLength,
}
if node.codelets:
node_data["codelets"] = node.codelets
nodes.append(node_data)
# Build link data with type classification
links = []
for link in slipnet.sliplinks:
link_data = {
"source": node_to_id[link.source],
"destination": node_to_id[link.destination],
"fixedLength": link.fixedLength,
}
if link.label:
link_data["label"] = node_to_id[link.label]
# Determine link type
if link in link.source.lateralSlipLinks:
link_data["type"] = "slip"
elif link in link.source.lateralNonSlipLinks:
link_data["type"] = "nonSlip"
elif link in link.source.categoryLinks:
link_data["type"] = "category"
elif link in link.source.instanceLinks:
link_data["type"] = "instance"
elif link in link.source.propertyLinks:
link_data["type"] = "property"
links.append(link_data)
# Identify initially clamped nodes
initially_clamped = [node_to_id[n] for n in slipnet.initiallyClampedSlipnodes]
# Create the full structure
data = {
"description": "Copycat Slipnet - A semantic network for analogical reasoning",
"nodeCount": len(nodes),
"linkCount": len(links),
"initiallyClampedNodes": initially_clamped,
"nodes": nodes,
"links": links,
}
return data
if __name__ == '__main__':
data = export_slipnet()
output_file = 'slipnet.json'
with open(output_file, 'w') as f:
json.dump(data, f, indent=2)
print(f"Exported {data['nodeCount']} nodes and {data['linkCount']} links to {output_file}")