Mexican Dollar
The “What’s the Dollar of Mexico?” problem is a classic demonstration of analogical reasoning with hypervectors. It shows how structured knowledge about countries can be encoded, and how algebraic operations can answer analogy questions without explicit programming.
The Problem
Given knowledge about three countries:
| Country | Code | Capital | Currency |
|---|---|---|---|
| USA | USA | Washington DC | Dollar |
| Mexico | MEX | Mexico City | Peso |
| Sweden | SWE | Stockholm | Krona |
We want to answer questions like:
- “What is the Dollar of Mexico?” → Peso
- “What is the Washington DC of Mexico?” → Mexico City
- “What is the Dollar of Sweden?” → Krona
How It Works
Each country is encoded as a bundled set of role-filler bindings:
To find “the Dollar of Mexico”, we compute a transfer vector from US to Mexico:
Then apply it to Dollar:
The result will have high overlap with Peso — the analogical answer.
The same transfer works for Sweden:
Code (Manual)
The algebraic approach — compute the transfer vector directly:
from kongming import hv
model = hv.MODEL_64K_8BIT
so = hv.SparseOperation(model, "knowledge", 0)
# Create role markers
country_code = hv.Sparkle.from_word(model, "role", "country_code")
capital = hv.Sparkle.from_word(model, "role", "capital")
currency = hv.Sparkle.from_word(model, "role", "currency")
# Create fillers
usa = hv.Sparkle.from_word(model, "country", "usa")
mex = hv.Sparkle.from_word(model, "country", "mex")
swe = hv.Sparkle.from_word(model, "country", "swe")
dc = hv.Sparkle.from_word(model, "capital", "dc")
mexico_city = hv.Sparkle.from_word(model, "capital", "mexico_city")
stockholm = hv.Sparkle.from_word(model, "capital", "stockholm")
dollar = hv.Sparkle.from_word(model, "currency", "dollar")
peso = hv.Sparkle.from_word(model, "currency", "peso")
krona = hv.Sparkle.from_word(model, "currency", "krona")
# Encode each country as role-filler bundles
us_record = hv.bundle(hv.Seed128.random(so),
hv.bind(country_code, usa),
hv.bind(capital, dc),
hv.bind(currency, dollar),
)
mexico_record = hv.bundle(hv.Seed128.random(so),
hv.bind(country_code, mex),
hv.bind(capital, mexico_city),
hv.bind(currency, peso),
)
sweden_record = hv.bundle(hv.Seed128.random(so),
hv.bind(country_code, swe),
hv.bind(capital, stockholm),
hv.bind(currency, krona),
)
# Transfer vector: Mexico / US
transfer_to_mexico = hv.release(mexico_record, us_record)
# "What's the Dollar of Mexico?"
mexican_dollar = hv.bind(dollar, transfer_to_mexico)
print(f"peso overlap: {hv.overlap(mexican_dollar, peso)}") # high!
print(f"dollar overlap: {hv.overlap(mexican_dollar, dollar)}") # ~1 (noise)
print(f"krona overlap: {hv.overlap(mexican_dollar, krona)}") # ~1 (noise)
# "What's the Washington DC of Mexico?"
mexican_dc = hv.bind(dc, transfer_to_mexico)
print(f"mexico_city overlap: {hv.overlap(mexican_dc, mexico_city)}") # high!
# Transfer to Sweden works the same way
transfer_to_sweden = hv.release(sweden_record, us_record)
swedish_dollar = hv.bind(dollar, transfer_to_sweden)
print(f"krona overlap: {hv.overlap(swedish_dollar, krona)}") # high!
Code (with AnalogicalReasoner)
When records are stored in memory (as Octopus composites), the AnalogicalReasoner selector handles the transfer automatically:
from kongming import hv, memory
model = hv.MODEL_64K_8BIT
store = memory.InMemory(model)
keys = ["capital", "currency", "country_code"]
# Store individual fillers — NNS needs them as searchable items
fillers = {}
for word in ["dc", "USD", "USA", "mexicoCity", "MXN", "MEX",
"stockholm", "SEK", "SWE"]:
s = hv.Sparkle.from_word(model, 0, word)
store.put(s)
fillers[word] = s
# Store country records as Octopus composites
store.put(hv.Octopus(
hv.Seed128("country", "USA"), keys,
fillers["dc"], fillers["USD"], fillers["USA"],
))
store.put(hv.Octopus(
hv.Seed128("country", "MEX"), keys,
fillers["mexicoCity"], fillers["MXN"], fillers["MEX"],
))
store.put(hv.Octopus(
hv.Seed128("country", "SWE"), keys,
fillers["stockholm"], fillers["SEK"], fillers["SWE"],
))
# Retrieve stored records
us_code = store.get("country", "USA").code
mex_code = store.get("country", "MEX").code
swe_code = store.get("country", "SWE").code
view = store.new_view()
# "What is the USD of Mexico?"
result = memory.first_picked_chunk(view,
memory.nns(
memory.analogical_reasoner(
memory.with_code(mex_code),
us_code,
fillers["USD"],
)
)
)
print(result.id) # → ✨:🌱MXN
# "What is the Washington DC of Mexico?"
result = memory.first_picked_chunk(view,
memory.nns(
memory.analogical_reasoner(
memory.with_code(mex_code),
us_code,
fillers["dc"],
)
)
)
print(result.id) # → ✨:🌱mexicoCity
# "What is the Dollar of Sweden?"
result = memory.first_picked_chunk(view,
memory.nns(
memory.analogical_reasoner(
memory.with_code(swe_code),
us_code,
fillers["USD"],
)
)
)
print(result.id) # → ✨:🌱SEK
The AnalogicalReasoner computes the transfer vector internally and uses near-neighbor search to find the best match in memory — no manual algebra needed.
Why It Works
The transfer vector captures the structural mapping between the two records. When applied to any filler from the US record, it maps it to the corresponding filler in the Mexico record — because the role-filler binding structure is preserved by the algebra.
This is a form of analogical reasoning: no explicit rules, no lookup tables — just algebraic operations on high-dimensional vectors.
See Also
- Concepts: Operators — algebraic foundations
- Operators — bind, release, bundle
- Octopus — key-value composite used for country records
- Memory: Selectors —
analogical_reasoner,nns,with_code - Near-neighbor search — how the reasoner finds answers