10  Distributed representations, similarity, and recognition

In the previous chapter, we were introduced to the Exemplar-Based Random Walk (EBRW) model. We saw how it could be used to model behavior in a recognition memory task in which a participant studies a number of items and is then presented with a “probe” item and must decide whether or not the probe was among the items that were studied. The EBRW explained such decisions in terms of summed similarity between the probe and each studied item. In the EBRW, similarity was a function of the distance between representations of the probe and study items. The probe and study items were represented as points in a multidimensional space. Each dimension of this space corresponds to a “feature” or “attribute” that an item could have, with different coordinates corresponding to different values of that feature/attribute. Often, these features/attributes can be identified with particular physical characteristics of an item (like hue, brightness, roughness, etc.).

The EBRW is a great example of how a computational cognitive model can help explain behavior in terms of latent representations that someone forms of their experience. It is also an example of a distributed representation of an item, in that an item is characterized by the particular distribution or configuration of values it has across a number of features/attributes. In this chapter, we explore more general forms of distributed representations and more general ways that we can model similarity between representations. We will continue to model representations of items as sets of numbers—that is, as a vector—but we will abandon the notion that each element of the vector has a direct relationship with physical characteristics of the item.

10.1 Representations

While it is beyond the scope of our course to delve into the philosophy behind the idea of “representation”, it is worth spending some time to think about what a “representation” is in the context of a cognitive model, so that we can at least arrive at a reasonable operational definition of the term. For additional discussion, see [].

For our purposes, we can define a representation as a formal entity posited by a cognitive model that enables certain kinds of processes that, when applied to that representation, enable the model to make predictions about performance in a particular task context. You may notice that this definition of a representation is tied to both the processes posited by a model as well as the kinds of predictions the model is expected to make. This is because a representation only has meaning in the context of these other aspects of the model.

We can think of a representation as a kind of “code” that conveys information that can be “read” by a suitable process. To take a concrete, if not exactly cognitive, example, consider the task of sorting mail. Imagine that we are working in a distribution center that receives packages that are intended to go to different parts of the country. Our job is to route those packages to the post office nearest their final destinations. We can accomplish this task by reading (processing) the ZIP codes on each package. The ZIP code is a numerical representation of the geographical area for which a package is destined, which when processed by an appropriate reader enables them to route the package to the correct office. This example illustrates the essential characteristics of a representation:

  • A ZIP code only serves as a representation of geographical area when processed appropriately. If you don’t have sensory organs to see the numbers or you do not know how to interpret the numbers, the ZIP code is meaningless. As noted above, a representation only has meaning in the context of the processes applied to it to accomplish a task.
  • There isn’t necessarily just one way to represent something. We could write ZIP codes with Roman numerals instead of Arabic numerals—while this would entail changing the kind of process applied to the ZIP code (because the two formats must be read in different ways), both are legitimate ways of representing geographical regions in the context of the mail-sorting task. We could even use a graphical representation, like a map with a dot indicating the destination. Again, such a representation would need to be processed appropriately.
  • The structure of a representation may or may not enable multiple kinds of processes to be applied to it, potentially to serve different tasks. For example, ZIP codes do not just serve as labels for different regions—nearby regions have similar ZIP codes. This is because the earlier digits indicate broad geographic regions while later digits represent narrower subdivisions of those larger regions. Therefore, one could apply a comparison process to these ZIP codes to determine not just whether two packages were going to the same region, but whether they were going to nearby regions. This would enable sorting to be robust to certain kinds of contingencies. For example, if the post office in one region were closed, the comparison process could allow you to determine the nearest open office to send the package to. A graphical representation of the postal destination would allow for even finer gradations of similarity.
  • Positing a representation does not necessarily commit to any particular way that such a representation may be implemented. Here, I use the term “implementation” in the same way as in the three levels of description posited by Marr (1982). A ZIP code can be “implemented” as ink on a page, as pixels on a screen, as an etching on a tablet, as a sound wave (if spoken aloud), etc. While these different implementations of a ZIP code would entail different implementations of how they were processed, they do not alter the form or content of the ZIP code. The broader point is that the kinds of representations posited by cognitive models are defined not by their physical forms, but rather by their abstract structure and the kinds of processes they support.

We can see how these principles manifested in the random walk/diffusion/race models we explored earlier. As we discussed, these models represent a decision-maker’s current state of mind in terms of one or more numbers that represent the degree to which the decision-maker favors each option they are choosing between. Representing evidence as a number enables an evidence accumulation process that can be modeled via the mathematical operation of addition. These models do not necessarily claim that the numbers that represent accumulated evidence are “implemented” in any particular way. That said, as we will see later in this course, it is possible to relate representations of accumulated evidence to scalp (Philiastides et al., 2006) and single-neuron (Purcell et al., 2010; Purcell et al., 2012; Shadlen & Newsome, 2001) electrophysiology as well as fMRI BOLD signals (Turner et al., 2013).

10.2 Types of vector representations

Many cognitive models make use of representations that take the form of vectors, that is, as ordered collections of numbers. We saw one kind of vector representation in the EBRW. In the EBRW, each item was represented as a vector of coordinates that specified that item’s position within a multidimensional space. That representation enabled the EBRW to operationalize similarity as a function of distance between vector representations of items. We now consider other broad classes of vector representations and, in the next section, we see how different kinds of representations enable other operationalizations of similarity. Finally, we see how similarity can be used to model performance in different kinds of tasks.

In the examples below, to keep things concrete, let’s imagine a participant is engaged in a task that involves making judgments about different concepts. There are eight concepts, each of which is a living thing from a particular category (these concepts are the same ones used in the examples from Rogers & McClelland (2004)):

  • Pine
  • Oak
  • Rose
  • Daisy
  • Robin
  • Canary
  • Sunfish
  • Salmon

You may notice that these concepts can be grouped together in different ways. For example, we could divide them up into plants and animals; into trees, flowers, birds, and fish; into things that are red, green, or yellow; etc. The kinds of vector representations we consider below will illustrate how these relations can be encoded in the vectors.

10.2.1 Localist representations

Perhaps the simplest and most direct way to represent each of the eight concepts above is to use a localist representation. The term “localist” refers to the idea that there is a one-to-one mapping between each concept and its “location” within a vector. Specifically, with a localist representation, each vector has as many entries as there are things to be represented. So if we have eight concepts, each of them will be represented with an eight-dimensional vector. Each of those vectors will contain zeros except for a “1” in exactly one entry in the vector. The “location” of the 1 is what determines which concept the vector represents.

The matrix below gives an example of a set of localist representations of each of the eight concepts above. Each row is the representation of a different concept.

Code
concept_names <- c("Pine", "Oak", "Rose", "Daisy", "Robin", "Canary", "Sunfish", "Salmon")
category_names <- rep(c("Tree", "Flower", "Bird", "Fish"), each = 2)

rep_localist <- diag(length(concept_names))
rownames(rep_localist) <- concept_names

print(rep_localist)
        [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
Pine       1    0    0    0    0    0    0    0
Oak        0    1    0    0    0    0    0    0
Rose       0    0    1    0    0    0    0    0
Daisy      0    0    0    1    0    0    0    0
Robin      0    0    0    0    1    0    0    0
Canary     0    0    0    0    0    1    0    0
Sunfish    0    0    0    0    0    0    1    0
Salmon     0    0    0    0    0    0    0    1

Note that there is nothing special about having the 1’s on the diagonal—or even about using 1’s in the first place! For example, the matrix below is another example of localist representations of the same set of concepts:

Code
alt_rep_localist <- rep_localist %*% diag(rpois(n = nrow(rep_localist), lambda = 5) + 1)
alt_rep_localist <- alt_rep_localist[sample(nrow(alt_rep_localist)),]
rownames(alt_rep_localist) <- concept_names

print(alt_rep_localist)
        [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
Pine       0   12    0    0    0    0    0    0
Oak        0    0    0    0    0    0    0    7
Rose       0    0    0    0    0    0    4    0
Daisy      0    0    0    5    0    0    0    0
Robin      0    0    0    0    0    5    0    0
Canary     0    0    5    0    0    0    0    0
Sunfish    0    0    0    0    2    0    0    0
Salmon     4    0    0    0    0    0    0    0

We could even replace all the 0’s with NAs or -99 or any other value that we agree to interpret as a kind of “background”. Ultimately, what matters about a localist representation is that what a vector represents is indicated by which entry in the vector is “not background”.

10.2.2 Separable distributed representations

Another defining characteristic of a localist representation is that it does not allow for gradations of similarity. Either two representations refer to the same thing or they refer to different things. Distributed vector representations, like those used in the EBRW, allow for representations to be partially similar to one another.

Although it is not necessarily standard terminology in the field, I think it is important to distinguish between separable and integral distributed representations. The difference between them is that, with a separable distributed representation, it is possible to identify the elements of the vector with particular attributes/features/dimensions of the items being represented. With an integral distributed representation, the individual elements of the vector have no interpretation on their own—instead, different items are represented by different patterns of values across all the elements of a vector. This distinction probably seems pretty abstract, so let’s dig into some concrete examples.

Returning to the set of concepts above, one way to construct a set of separable distributed representations is to consider different attributes that each concept may or may not have. Each element of the vectors corresponds to a different attribute. For each concept, its representation will have a value of 1 in the entries corresponding to attributes that the concept possesses and a value of 0 in the entries corresponding to attributes that the concept lacks. This is illustrated below:

Code
attribute_names <- c("can_grow", "is_living", "has_roots", "has_bark", "is_big", "has_branches", "is_green", "has_leaves", "has_petals", "is_pretty", "is_red", "is_yellow", "can_move", "has_feathers", "can_fly", "has_wings", "can_sing", "can_swim", "has_gills", "has_scales")

rep_distrib_sep1 <- matrix(c(
    1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1
), nrow = 8, ncol = length(attribute_names), byrow = TRUE, dimnames = list(concept_names, attribute_names))

knitr::kable(rep_distrib_sep1)
can_grow is_living has_roots has_bark is_big has_branches is_green has_leaves has_petals is_pretty is_red is_yellow can_move has_feathers can_fly has_wings can_sing can_swim has_gills has_scales
Pine 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
Oak 1 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0
Rose 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0
Daisy 1 1 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0
Robin 1 1 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0 0 0
Canary 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0
Sunfish 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1
Salmon 1 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 1

Although we will consider similarity more deeply in the next section, note that the distributed representations above make it possible to judge similarity based on the extent to which two concepts’ representations do or do not share attributes. Indeed, the different categories (plants/animals) and subcategories (trees/flowers/birds/fish) to which the concepts belong are implicit in which attributes are shared between concepts.

In the example above, each entry in the vector corresponded to a distinct attribute. It is also possible to construct separable distributed representations where attributes are encoded with sectors of a vector. For example, rather than treating is_red, is_yellow, and is_green as different attributes, we might assign a “sector” consisting of several entries to represent the color associated with a concept. Maybe that sector consists of three entries, for hue, chroma, and lightness, as in the example at the beginning of the EBRW chapter.

Relatedly, distributed representations can use continuous values—they are not restricted to binary indicators of the presence/absence of an attribute. For example, instead of an attribute called is_big, we could have a size attribute which corresponds to the size of the concept being represented.

For example, here is how one might represent color and size for each of the concepts in our running example:

Code
alt_attribute_names <- c("size", "hue", "chroma", "lightness")

rep_distrib_sep2 <- matrix(c(
    50, 131, 36, 27,
    40, 20, 60, 16,
    0.1, 3, 58, 79,
    0.2, 52, 100, 50,
    0.3, 9, 63, 38,
    0.1, 60, 100, 63,
    0.5, 32, 100, 59,
    1, 5, 99, 71
), nrow = 8, ncol = length(alt_attribute_names), byrow = TRUE, dimnames = list(concept_names, alt_attribute_names))

knitr::kable(rep_distrib_sep2)
size hue chroma lightness
Pine 50.0 131 36 27
Oak 40.0 20 60 16
Rose 0.1 3 58 79
Daisy 0.2 52 100 50
Robin 0.3 9 63 38
Canary 0.1 60 100 63
Sunfish 0.5 32 100 59
Salmon 1.0 5 99 71

With a separable distributed representation, we can increase the dimensionality of the vectors as much as we want. For example, a separable distributed representation might consist of hundreds of dimensions. However, it is unlikely that all of these dimensions would be relevant to any particular task. There may also be capacity limits on the number of dimensions that someone could make use of at any particular time. This highlights one final, but important, aspects of a separable distributed representation: Because different kinds of information about an item are represented in different elements of the vector, separable distributed representations enable us to model attention to particular kinds of information. Specifically, as we shall see below, we can assign weights to each dimension that represent the degree to which someone attends to that dimension within a given task context. A separable distributed representation can thus support performance in many different tasks by assuming that some tasks entail attention to different elements of that representation.

10.2.3 Integral distributed representations

The final kind of vector representation we will consider are integral distributed representations. The difference between integral and separable distributed representations is that the vector elements of an integral representation cannot be identified with distinct features/attributes of the items. Rather, an item’s representation consists of the complete pattern of elements across the vector. An integral representation can thus be thought of as a “holistic” or “configural” representation, because its individual elements can only be understood as part of the complete pattern of entries in the vector.

An analogy may help clarify this admittedly abstract issue: Consider baking a cake. Before you bake the cake, its ingredients are separable: You have a pile of flour, a carton of eggs, a jug of milk, etc. One could imagine constructing a separable distributed representation of the pre-baked cake in which each element corresponded to a particular ingredient and the entries specified the quantities of each ingredient. However, once the ingredients are mixed and the cake is baked, it is no longer reducible to its component ingredients. Those same ingredients, in those same quantities, would not necessarily result in the same cake—it is the particular way in which the ingredients are combined and prepared that results in the final product. The way any individual ingredient contributes to the baked cake depends on its relationship to all the other ingredients in the cake. In that sense, the baked cake is best represented using an integral as opposed to separable representation.

Unlike separable representations, then, integral representations make it impossible to model giving different amounts of weight/attention to different attributes of an item. This is an important distinction, and is why I adopted the terms “integral” and “separable” to distinguish between these kinds of representations. Garner & Felfoldy (1970) used those terms to distinguish between cases in which a participant could selectively attend to one feature while ignoring others (separable) and cases in which a participant could not ignore other features (integral).

It is still possible to conceive of similarity between integral representations. It is just that similarity depends not on sharing specific elements of a vector, but instead on having similar patterns of values across elements. To visualize this, we can graph a vector representation with the index of the elements along the horizontal axis and the value on the vertical axis. Integral representations are similar to the extent that the resulting graphs have similar shapes. This is illustrated below.

Code
x <- rnorm(n = 10)
x <- (x - mean(x)) / sd(x)

s_vals <- round(seq(0, 1, length.out = 6), 2)

toPlot <- c()

for (s in s_vals) {
    if (s < 1) {
        while (TRUE) {
            y <- rnorm(n = 10)
            y <- (y - mean(y)) / sd(y)
            
            if (round(cor(x, y), 2) == s) break
        }
    } else {
        y <- x
    }
    
    toPlot <- rbind(
        toPlot,
        tibble(sim_factor = paste("Similarity =", s), rep = "A", i = 1:length(x), val = x),
        tibble(sim_factor = paste("Similarity =", s), rep = "B", i = 1:length(y), val = y)
    )
}

toPlot %>%
    ggplot(aes(x = i, y = val, color = rep, linetype = rep)) +
    geom_line() +
    facet_wrap("sim_factor") +
    labs(x = "Vector index", y = "Value", color = "Representation", linetype = "Representation", title = "Similarity between integral representations")

It will not have escaped your notice (especially if you look at the code for the above chunk) that similarity between integral representations can be modeled in terms of their correlation. We will explore this more below.

For now, we can return to our running example to see what integral distributed representations of our eight concepts might look like. In fact, the example representations below are based on ones derived from a statistical model of word co-occurrence called Latent Semantic Analysis (Landauer & Dumais, 1997). As noted below, many machine learning models make use of integral distributed representations, and some of these are even plausible cognitive models of learning, which we will explore in the next chapter.

Code
load("lsa_reps_examples.rdata")
rownames(lsa_reps_examples) <- concept_names

knitr::kable(lsa_reps_examples)
V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40 V41 V42 V43 V44 V45 V46 V47 V48 V49 V50 V51 V52 V53 V54 V55 V56 V57 V58 V59 V60 V61 V62 V63 V64 V65 V66 V67 V68 V69 V70 V71 V72 V73 V74 V75 V76 V77 V78 V79 V80 V81 V82 V83 V84 V85 V86 V87 V88 V89 V90 V91 V92 V93 V94 V95 V96 V97 V98 V99 V100 V101 V102 V103 V104 V105 V106 V107 V108 V109 V110 V111 V112 V113 V114 V115 V116 V117 V118 V119 V120 V121 V122 V123 V124 V125 V126 V127 V128 V129 V130 V131 V132 V133 V134 V135 V136 V137 V138 V139 V140 V141 V142 V143 V144 V145 V146 V147 V148 V149 V150 V151 V152 V153 V154 V155 V156 V157 V158 V159 V160 V161 V162 V163 V164 V165 V166 V167 V168 V169 V170 V171 V172 V173 V174 V175 V176 V177 V178 V179 V180 V181 V182 V183 V184 V185 V186 V187 V188 V189 V190 V191 V192 V193 V194 V195 V196 V197 V198 V199 V200 V201 V202 V203 V204 V205 V206 V207 V208 V209 V210 V211 V212 V213 V214 V215 V216 V217 V218 V219 V220 V221 V222 V223 V224 V225 V226 V227 V228 V229 V230 V231 V232 V233 V234 V235 V236 V237 V238 V239 V240 V241 V242 V243 V244 V245 V246 V247 V248 V249 V250 V251 V252 V253 V254 V255 V256 V257 V258 V259 V260 V261 V262 V263 V264 V265 V266 V267 V268 V269 V270 V271 V272 V273 V274 V275 V276 V277 V278 V279 V280 V281 V282 V283 V284 V285 V286 V287 V288 V289 V290 V291 V292 V293 V294 V295 V296 V297 V298 V299 V300 V301
Pine 57.307893 -33.412359 16.2100709 44.705718 5.4170146 -77.312664 -14.9572611 -27.9986255 -32.299533 -14.5052352 -4.5012929 -14.7266570 -16.1968384 -2.7994879 22.4571901 15.729012 11.851067 24.6211851 -9.1846293 2.2328819 -50.0163069 -3.0823218 33.7986198 3.119457 -17.114383 13.4889538 4.2553778 -1.5470188 -0.0345441 -18.7060762 -5.5927648 -3.3022175 -8.9941651 -1.8332407 22.5889364 13.926796 -9.5462912 0.7528040 20.7099540 8.977873 -18.0528367 13.0251756 5.1726928 12.223609 1.9835759 16.4832762 14.7923809 -2.818340 9.3103527 -0.0929313 -2.4702942 -25.6337961 14.202371 -11.0013780 -15.1385398 20.0835240 -0.0384213 -17.011600 14.5557277 -3.6187865 -8.1690392 9.0190558 6.2733016 -6.4742631 -21.835881 7.759060 9.4504609 -9.293384 2.3812821 13.5068984 2.3493417 6.492563 11.2623955 -7.2021843 4.4342641 4.404997 -3.6185602 4.560521 -5.6883207 0.3942039 -3.103478 -0.2551315 -7.4488074 1.048294 -1.7003834 -14.8334574 -7.3806372 21.785405 -6.706319 -14.7996623 -14.8057805 4.2287031 -6.268469 -9.6626014 3.6745613 -25.201119 5.2614205 -0.3076832 13.4564618 -0.2724789 -0.7343194 3.7806412 -4.0303417 6.906578 -16.8387944 -13.0173042 2.5265882 -2.1689518 3.9887857 -7.5873560 13.2403690 -6.8157585 -5.555756 -9.843402 6.9697738 1.149107 -0.7261884 13.9617517 -1.0806520 -0.7193217 -4.611446 0.6665858 -6.824130 -10.490425 -0.4190986 6.6339878 7.8565984 0.0106521 2.7727858 0.2248497 -3.6600560 -0.4946363 -13.6349074 -11.496180 -10.7151125 -9.0431384 8.1208173 -2.457928 -6.4496081 -3.1466380 0.1797440 -10.4487262 -3.0656779 1.3713914 7.7817654 2.2848116 3.4939900 -4.0449003 -0.4560466 4.2031594 -6.6603234 -6.8989429 0.6610100 -2.7081900 1.3808937 -8.3187305 4.1863844 -8.4271535 9.202663 13.9739600 -5.6569982 8.1442349 -0.4116793 -2.4405695 14.0171390 1.2796313 -4.2112780 -5.6927812 -1.065173 -14.3548774 -0.6837453 -8.4956908 4.7990248 -13.9831855 6.1447592 -6.668499 -1.9669608 8.8483279 -9.9708037 -1.5526994 -2.3869305 2.7068987 -5.8647411 3.9524734 4.062503 -3.9309064 -7.8845501 7.1579054 0.1585556 -5.5554894 9.0703253 -4.0248275 -6.9605902 -9.8685993 -0.3081633 0.4980695 3.7748428 1.9719101 5.0909790 0.1292172 -3.226018 9.6870012 -1.3666593 2.211289 -0.8944688 -0.8549064 3.0816563 4.9719423 4.114711 -14.281108 -7.9345617 -1.6437555 -7.9618872 -8.0368606 3.4011152 -2.7222087 2.8836967 0.2547526 -2.3780604 -1.1224880 -11.5821939 4.0609673 -8.6903496 -2.8427766 -2.4340404 4.2274566 -8.3796828 5.6594842 2.7963812 0.2636938 -9.7927961 9.5555720 9.4439495 1.522367 -6.7097545 -0.3524096 -0.8597147 6.5798253 2.2472588 8.0601951 -5.9025702 -2.5059219 2.0198455 -12.8518396 2.291976 1.1620025 6.2732416 2.8002452 -1.6263764 2.6704350 -10.1605403 -2.7630097 12.7147504 5.715203 1.8822445 7.2482598 2.3230484 -5.3064914 8.0907059 3.4526805 -8.4392930 4.3845380 -2.7214840 13.0781151 4.9657986 4.3581377 -7.6347574 4.2915669 1.3878334 3.9236583 2.0851763 -9.9306471 -2.457062 11.5000748 6.9552347 -0.2530878 10.3370600 -5.9768839 -8.2311500 -3.3333605 7.1425790 2.4735721 0.0097459 2.8663022 -3.1668199 1.2567023 -0.6361538 -6.8877328 11.1474688 -3.9729353 -1.8673939 -1.0748327 6.478546 12.9997228 10.3136090 -5.5700471 -9.9853885 9.1411329 0.2742870 -1.6717776
Oak 80.828697 -43.005475 26.7473579 33.209697 10.2143428 -88.688440 -26.7698885 -36.4809668 -32.689389 -28.1914601 5.8729877 -20.1327581 -26.6134246 16.4039523 15.2154146 13.858923 -16.996326 -0.0919365 10.3958523 19.4280717 -48.1713132 -10.4005373 16.1410747 -2.868989 -28.312146 2.4767914 -8.9061867 -5.9204197 4.4135992 -17.6132436 -24.8680966 11.0406067 -22.7716040 -1.1670161 12.7757840 5.076753 4.1300678 -5.0944729 47.6587007 6.015303 -19.0976943 1.9509617 -4.9462611 4.171989 3.3572475 20.1556112 10.8333810 -7.790407 2.5772871 -20.7119218 5.0779574 -19.1404229 22.226832 -22.0977128 -8.9922374 18.7208032 -4.4950088 -15.808647 22.7718473 -7.4013208 10.9007914 13.8910809 24.5008176 0.2974784 -6.888492 -4.040368 17.0215756 -21.543589 6.4547801 1.7278160 6.0658790 11.427325 13.5814514 -7.5986843 -0.4017347 12.109434 -11.1031793 3.234420 11.4574224 -10.0867276 -10.302973 -18.1461301 -5.2251757 18.880036 -8.3391278 -6.6289327 -0.6804034 10.682985 -9.124614 -9.7054687 -4.9089148 -16.4950604 1.187639 -4.1632332 -2.1032415 -18.275406 2.3416492 -7.5896618 24.0136987 -3.2429851 6.9687686 14.4519253 -0.2791838 7.115823 -11.9696475 -1.5205516 -8.2146305 7.9105197 14.0550096 -2.9324309 22.6483777 10.3098669 -2.724509 2.098667 4.5523924 -10.880036 0.5708622 9.9182861 -9.6121785 -8.8592534 0.124189 16.0831198 -7.280793 -7.470394 15.9237948 7.6834953 12.3278255 -3.5486672 -11.9662029 -4.9252451 -2.1536257 1.8985313 -7.5979244 -7.383110 -19.0073754 1.9448647 2.0579126 -17.412922 -11.1693916 -14.6267846 -4.1773881 -12.7152303 8.6602983 8.8907915 3.0503019 -0.0037553 -9.8574560 4.4583720 -3.1306854 -9.1026034 -6.1067185 -10.6015973 -5.7251255 -10.5043480 0.8605427 -12.5711306 3.9891822 -14.5223856 2.131734 8.6427481 -4.4826514 3.5473972 -1.4149334 -9.7085635 16.2090817 -0.1036665 -10.3853703 -6.5985241 -5.582096 -5.1657445 -8.3063971 -18.5980656 12.2062214 -6.8818838 5.1119193 7.313527 -6.2993178 9.0798866 -13.2630747 4.6537671 3.7365609 -1.4956865 -12.0706154 0.9103531 -1.686001 -2.8324344 -6.7086971 12.9250006 11.1620524 -9.8040349 12.2273438 6.4498272 -6.0560375 -8.9560154 11.3814081 17.0410777 -0.3908397 12.1629579 10.2282912 13.1237576 8.497552 7.8198934 -11.0312620 -1.332901 -13.9372947 6.5161129 1.5215513 -5.0862092 -5.332131 -8.985470 -3.0928814 -11.8218450 -8.3140759 -9.1598165 0.7849052 -1.6985827 4.3136719 -1.6546882 1.9974886 -9.4080705 -19.5365685 -0.6712322 -17.2954168 0.4360413 1.9430895 16.7224478 -12.1301834 13.3619001 -9.1417729 -4.3495371 -20.9839851 3.0265527 21.3448865 20.042162 -3.9763363 -7.7227427 -4.3354595 1.5552491 3.5565718 7.3571507 4.9196287 -6.7190032 15.4551298 -18.3392176 -7.329798 19.9773636 3.9836644 -9.9018899 -2.4813813 0.8674818 -10.0296373 5.1682120 14.7502598 -2.908291 3.3245893 -1.7032331 13.9013417 3.4384251 7.8857765 -8.5167114 -18.8396904 5.5098333 -2.5232538 12.7119670 -2.0198069 11.5662823 -8.4465002 12.6920782 8.5115727 5.0622426 1.0143331 -10.7399950 -5.023741 13.8779947 7.6182755 -14.4531521 5.6044493 -2.8049467 -11.8018653 4.2484054 -3.9074604 -7.9172866 -3.5020140 8.1817810 3.6104357 0.2792197 2.6597445 -10.6930430 19.5662373 -11.0043755 6.7610438 1.8715504 5.841162 12.3294527 -0.5637173 1.4770438 -13.3556489 5.2794818 3.9390143 -5.9196026
Rose 169.793825 -98.482982 7.9087297 -33.526768 -15.3870192 -12.804220 -14.4783884 -15.5809105 -25.410565 -19.4996026 -27.4623533 -5.6971808 -8.5937710 28.3714423 29.5112569 -7.504715 -12.272873 -14.9184604 48.8658440 1.8716615 -14.6257486 -29.9462025 -2.6105517 -18.737203 5.911870 24.5004368 3.5533200 27.8024060 -2.2104456 -27.5325946 5.3811664 14.3092581 -4.2505634 6.2236096 30.3394288 8.797142 4.6484107 4.6574179 10.1908145 -5.267450 -19.6150691 -23.6457392 29.0449034 -28.486535 7.6362859 -0.1406000 -5.7477637 -2.528637 23.5909689 -14.3507716 18.2506426 -9.4865387 12.052128 -15.1619448 14.9628981 9.3703101 -11.4976291 3.435361 7.0759125 -1.4774759 -0.0685737 -16.2138107 -11.4504387 -11.0340333 -8.777626 10.978064 31.6275187 -17.394848 1.5170053 -12.4252284 3.1297145 9.534743 25.7245968 1.5104692 -12.8455362 21.783901 -33.4809698 22.691487 15.3734504 7.3714504 1.814490 -0.1285942 8.4247191 19.642460 -4.7169768 -0.1141606 7.3560021 14.891125 6.643697 -12.9119349 3.8057530 11.4236129 -7.610132 22.4005712 -11.1136624 -14.408629 -3.2702556 10.5295792 4.7816807 13.0524770 0.2997899 6.3874410 10.2777419 14.919367 15.9114001 2.6025231 11.2475550 10.1245306 -4.3021472 -14.1799770 29.2487634 15.3426564 7.777509 5.093669 -26.2353297 7.290247 -1.3080207 14.7794721 -7.3412600 1.5924531 10.437176 -7.2708055 3.825308 -4.408311 22.2402751 -9.7766258 12.9946723 5.5923931 -6.1653314 -14.2313198 19.3671305 19.4241150 4.6512561 -5.752324 -3.0811970 0.7296660 -11.6086048 3.939982 -9.5809516 -1.1101148 11.4602614 7.3187249 -0.9660889 -11.2094730 5.8177400 -1.7164509 12.0703200 2.6721368 -13.8695715 -8.8573739 -2.0379172 -0.0437098 -6.8562632 -10.4266609 1.9711819 -17.1435723 -21.0114836 -3.0988737 -16.277689 5.6989771 17.1905151 7.3003084 -13.0027768 -6.8507316 13.4156697 2.5991769 6.6928880 0.9784581 -15.818938 5.1605969 -7.9594016 8.7603460 -9.5441003 6.5391132 3.2680039 7.200926 8.5513808 -12.7086585 7.3750603 -14.1497674 6.1342401 -0.4422350 2.8880373 -13.0100570 -14.632730 15.6580307 -19.2117003 -6.3291255 -9.8000420 1.1788047 2.0219291 -6.1350549 -7.5339776 4.4261359 12.7294918 14.7959570 14.2741003 1.3313340 -3.6815732 13.1639548 6.393202 3.3457355 -0.3390432 2.370055 -26.2276702 1.8357318 0.4190367 2.3285473 1.788032 -1.154643 10.6305695 -6.9046237 -2.4252594 8.7147069 -10.0202057 12.2915791 -4.8637219 -8.3308660 14.7143327 -5.9119868 -3.5836339 -5.9821050 3.3685229 -0.5273490 7.4853536 0.8879983 -1.6465533 6.7061540 2.4660995 -17.8014362 3.0484707 -8.8926470 -5.8612257 -10.381385 1.3308735 -1.7600527 -6.8335712 2.2206121 -5.4044090 -0.9646472 -13.1956251 -0.5255635 6.2242313 -5.5270536 -3.950202 10.8664688 -13.9663561 0.0951356 -1.8206412 -8.7879439 -22.3673848 1.2170017 -14.5392483 5.334749 -0.3670962 -1.2099986 -6.3474973 22.8696146 2.1016206 -12.2466947 -3.1836825 -13.9297601 -19.3750175 -5.5130141 7.4440775 4.6176661 9.0544032 17.0450127 1.3686618 3.7586278 -7.5690573 -13.8882015 -2.576893 11.9090520 11.9572000 -1.8513407 -1.2740561 2.4682909 3.4724126 -7.5428997 -9.2694222 -6.5640788 -9.9982253 0.4708680 -2.7110109 2.2204115 -10.2996062 -1.0598588 11.1593595 11.8381842 5.0702978 13.6659472 -13.532357 12.3769665 -3.2224983 -4.2846200 7.7764724 -5.4564864 -1.6851974 -1.2794527
Daisy 24.100221 -16.222531 -9.3980192 3.055284 4.6509376 -2.064403 3.9375922 -7.1785296 -3.427622 -2.5476127 -3.1881560 -3.0890079 2.8138486 8.4137179 8.8892470 3.930178 4.484171 -5.4619650 3.4778137 -0.6353814 -15.1974376 -1.0285566 -1.0310829 -5.313575 6.470959 1.2715653 -2.6302464 -0.2207977 1.3919182 -2.1731995 -0.9797795 -0.1544892 1.0353388 4.5132537 -0.5794254 -3.784247 7.4763496 1.1541796 1.8444969 4.896767 0.4737606 0.9662965 -0.8882757 2.829369 2.2048359 5.3551305 1.7500490 -2.990434 7.6930077 -3.2830856 1.9311926 -1.5081203 6.648616 -8.3339914 2.1998600 9.1336778 2.9894294 -2.443459 2.7925253 1.1114895 3.0950249 -6.0302616 -1.7250197 2.7722301 -2.009590 -2.873179 5.3776300 -3.190599 -1.2582892 -1.1636990 -0.5336351 4.735501 3.0734966 1.4201545 -7.2246440 1.439099 -4.7856932 2.419344 4.3053005 -0.6889198 2.566885 0.1682647 1.8888079 3.535584 -2.7770019 0.7089246 4.0487488 2.837982 2.183721 -0.2072544 -2.7926343 1.6429740 -4.428295 7.5298984 0.7520422 -3.652488 2.1818781 4.1139360 2.6437537 2.8562345 1.6740233 -5.6797268 1.4680887 5.022289 0.0013254 0.9708033 -0.0881402 -0.3067673 -1.4011147 -0.0301427 1.7268919 0.2298842 1.751551 2.619544 0.1792596 2.262902 0.5520544 2.2583938 -3.4603345 -1.4617958 1.765299 -3.4695657 -1.789427 -1.291499 -0.9980570 -0.8156097 2.2177832 -1.2410956 0.1199769 1.2092455 0.5854205 0.6172075 1.0071439 -1.830293 0.6156090 -1.5444117 0.3443324 2.931205 -0.5220600 -0.3023792 -2.6408510 0.9592554 -0.6381676 -2.1317337 1.6269074 0.7034337 0.7778252 -0.6087897 1.0196182 -0.5708336 0.3365156 -0.4899099 0.1691228 -2.2193951 0.6475840 -1.3154586 -1.5273491 1.9772685 -1.502573 0.5563318 -0.9540241 -0.9424389 -0.1309414 0.5641224 4.9536121 -1.6481216 0.0152478 0.0713423 -2.476908 -1.2880931 -2.2792060 -1.5237614 1.8059194 -1.0313243 -1.8179311 1.719301 1.4999382 -0.8877665 -1.1326820 -2.6514381 -2.1634048 0.2419982 4.1322335 -1.7894117 -1.879960 -0.9275659 -2.8482647 0.0662491 -1.2305098 2.5804608 0.1833774 0.1125451 -2.4019557 1.3005379 -1.7227692 0.2017959 2.4216225 2.5192043 -0.0032864 -0.4333918 0.878485 -0.1422516 1.5043564 1.427500 -1.4644374 1.8093708 -1.3059904 1.9247803 2.011994 -0.836282 -0.0305426 2.6002665 -0.4163487 -1.1505782 0.5197413 -0.3288488 2.0073724 -3.3995149 0.3591165 -0.8033242 2.3073012 -2.4522559 1.0381770 0.5889167 -1.2192055 -2.2046264 -0.0626758 -2.2200307 1.7759482 -1.4519378 1.4079847 4.1210806 -1.1196130 -1.105506 1.3710091 -0.5470366 -0.4197090 -1.8541823 0.8415489 -3.7318444 2.5166220 3.6061232 4.2544035 1.8787912 2.367761 -0.3487660 0.2295378 -0.2703449 -0.4056041 -0.8336008 -0.9515302 -0.8328531 -0.2374453 2.148024 -1.5675426 1.2949289 1.6991554 2.7862258 -0.6770487 0.0717748 2.0054809 -1.4472863 0.4194559 -1.0473439 1.0471419 -0.8462998 -0.7288768 2.1141282 0.2206433 0.9258962 -3.9254093 -1.4988314 1.123979 -0.3680056 2.0287794 -4.2838177 0.5503843 -0.7195955 1.3857401 -0.7845520 -1.6141708 0.4472853 -0.7486416 0.7764512 -0.1349593 1.3673770 -1.5620602 -1.1320222 -1.6498093 2.6569546 -0.3293581 -0.7622562 1.200270 -0.4821121 -1.1639723 -0.9182759 -0.4077662 -1.6968441 -2.0445169 0.4338182
Robin 87.209927 -39.421553 -13.2598933 -19.255405 12.6966848 13.649532 1.5221174 -14.5741633 -12.210516 -0.5938838 -18.5349484 2.2500446 -21.2421595 24.6352050 13.8045869 32.620392 17.944238 -36.4383255 7.9661383 -5.6953062 -11.4241949 11.2369978 0.6669709 -8.619645 13.561825 6.9819243 18.0589694 2.5570000 -0.6621710 6.6970804 -8.7458538 -0.9545062 0.0350254 10.0853483 -12.8431858 -1.320653 2.0786364 -1.4873606 8.4382774 14.977747 -12.4116775 10.7415860 -4.5315644 13.927279 -0.7128135 9.6521416 -15.8944102 -10.277005 1.9953862 -4.9111056 13.1432152 5.2965675 6.968865 -10.1453840 6.4075715 23.5236111 8.0962490 1.989658 11.7967562 7.6318221 -3.2844709 4.1211137 -14.3982188 -10.9720064 -6.372903 4.284889 -2.0337362 -1.238032 -2.4289796 -5.6503061 -5.9904350 5.792373 6.6458773 1.3018887 10.0427329 5.481997 2.8851058 -5.935497 -7.9150823 3.4489882 9.693554 -10.6514120 3.4651870 -16.072585 -4.8761024 0.4401328 2.3540835 2.018782 -2.763589 -11.4212434 -2.3363155 -7.5630419 -8.602159 -4.2029689 7.3109632 -11.829872 -0.2461956 -2.6658087 3.1757153 -5.1620742 14.5710273 -10.9124843 -1.3028343 1.692319 -0.7856966 -18.7663053 1.2938884 7.7773864 -7.3547104 -3.8452535 3.5982890 -2.2197566 -2.964379 -1.904294 -2.5573230 3.325042 6.1901865 2.2946508 -8.6880534 -12.0032768 10.386573 -12.8842522 -6.411623 -7.892069 -2.3083960 -5.8206563 -0.6115528 -4.2427814 2.2741640 6.5603657 1.4889978 -7.4718740 -4.2095390 1.977323 8.5250592 -3.7560782 1.0639200 7.170260 -0.5884419 -4.8995161 -0.3838779 5.7217020 2.7197784 -0.1464315 4.8567756 -3.7900789 -12.7395744 -6.5280637 4.6569942 -5.3193175 3.3383670 -6.3656664 6.2351808 -7.2392041 2.8795302 -13.1776304 -3.7389721 3.4776007 4.965016 -0.1876198 -4.3859840 4.6497057 -2.7225727 0.0782279 5.5174978 -1.8113893 -3.6797836 2.8278217 3.474013 6.2143381 4.6871694 -3.9044960 -3.4702816 1.1007603 -6.5268288 2.856452 -1.4037336 -4.9919524 -3.4320218 -1.6192452 -5.3155054 -3.1168445 -2.8154924 -4.2488728 1.439867 3.6809335 -3.6949757 3.2758540 0.3023899 2.8939321 5.8187221 -0.6670275 3.0347652 -0.0887428 -1.5831982 2.3446869 2.5552661 7.5736351 -0.7490191 4.2969524 -1.069244 8.1020301 1.3164267 -4.474278 -0.9060467 3.3468269 -5.6638199 7.6678717 2.111567 -5.039989 0.6724506 5.3588127 -2.4365683 3.9894087 1.8377403 -1.5928317 0.2032277 -0.2400587 -7.0711414 -3.5950192 0.8553665 5.4162505 -1.4317203 3.0724923 -1.0762675 2.2648095 2.4612144 -1.0634334 -0.5904171 5.8193823 0.7524095 3.8338898 6.2357706 -2.316103 -1.3868023 -4.9612493 -0.7907146 2.9447086 -0.2930757 -0.4749651 2.5210642 6.3847007 11.3807706 10.8388429 -4.556101 -0.2353506 1.0616370 1.8722904 4.2907091 -2.4827531 -5.7486118 3.2385323 3.4846426 1.298343 2.8100805 4.2519181 -1.8560000 4.9559298 3.5121412 -3.1172622 -4.8691203 -7.0946331 -7.5602092 -3.0801421 -5.8546164 -2.2928990 -1.4201750 -2.1490260 -2.0374218 5.2978722 4.1143296 2.4964750 -1.707420 1.1130026 -1.4592996 -3.5824727 -5.0246880 -1.5280913 5.3745849 -3.4539094 -4.4424357 -7.0144081 2.4888395 -3.4601753 5.7682678 -0.5639159 1.7719846 1.8693498 0.3915249 4.3354162 0.6329579 -4.7893166 2.355965 -2.9944984 0.9746733 2.1831488 1.8503339 5.7226143 -3.5455370 -8.8605653
Canary 28.017936 -9.711131 1.8152580 4.152567 4.6799648 -18.097735 -9.7935685 -5.7242762 -9.117048 -6.4045831 -7.1737961 3.7290718 -4.2616369 -1.8423486 9.0168055 2.552417 10.890162 2.2268742 3.5813905 -10.7686105 0.9729619 3.9552726 9.7763952 4.992721 9.647722 3.4270408 4.9922738 -3.0303735 0.5653330 4.8307165 2.0812984 -1.3337595 5.0110176 3.8795221 1.9729774 3.114877 5.6659330 -0.3845149 -6.0508167 -2.505897 3.2621366 -8.5092034 0.2695424 8.999274 6.0097419 -8.0452811 -6.2318201 -2.956672 -8.0912288 10.2832121 -8.5757066 2.0593362 5.641815 -1.6180890 8.4030088 7.7012418 12.3991068 1.103800 -2.8683962 0.4302879 -1.0158241 -3.5594815 -4.2587670 -6.4801574 5.638990 6.314367 -0.0669196 -1.348402 7.5563033 -10.8832919 3.7249564 5.189974 -11.1306659 8.7928820 2.8876223 4.541898 -2.3243570 -3.231420 -0.4968872 3.5529275 2.423911 -3.5899662 -3.6125600 -8.406667 1.0438244 -3.3558637 -2.0500128 -3.263322 -1.852730 9.0978358 8.7565410 -1.1946464 4.118265 1.7952097 -4.1285179 -1.918781 0.3452547 -0.7890635 0.3350979 1.2202142 4.4832191 -6.2956360 1.7460564 6.924928 4.3928950 -1.8558423 4.9384971 -3.5288743 3.8039616 -8.2919066 3.7334474 2.7497758 3.204420 2.333834 -0.1390349 5.446273 -0.7796235 1.3121761 2.4666003 1.0968973 -1.472856 -0.4516848 -1.731479 1.297061 7.8931945 -7.8552598 -0.9213307 -5.1829622 1.4432436 -3.8580089 4.6614600 -0.4314485 2.3888149 -6.777383 -0.9086408 -0.2654383 0.5617019 4.655535 0.0676956 5.3238419 -1.3010668 -1.0045344 2.7937679 -1.5368508 -0.3815586 -0.8088542 -2.0564964 -0.5237565 3.4549695 4.1438910 2.4115136 3.6730358 2.9372325 0.8475813 -0.5392527 -5.5385215 -0.2500064 7.1198768 3.138690 5.7205305 -3.2763185 -1.6993905 0.0370527 4.1311516 5.1184535 -2.2756837 0.6948147 0.1812992 6.260929 1.2352646 2.8668889 2.6751753 1.6192647 2.6606578 0.5162005 3.506697 -0.3387404 4.1307986 -3.5253067 2.5762092 0.8551627 2.6942940 0.2475203 -3.6053549 3.826843 -1.4177310 1.6054711 -1.7910695 -6.5983420 -0.3165268 -2.0444003 3.5619750 4.1132019 -1.5378804 -6.9456183 7.5385905 8.1937005 4.2524765 -1.7145109 -1.0758415 -1.455281 6.5756390 5.8934214 -5.505482 1.5139850 4.7889436 1.2251997 3.1443918 1.363844 5.646660 -4.3012001 8.7334368 -1.7210364 -0.5361291 1.1349723 -5.3463749 -4.1545847 -4.0710196 1.3033721 -0.4549559 2.4057862 4.3525330 0.9562371 1.3003306 -0.9387649 -1.5592181 1.0699544 -0.3185847 2.1227863 2.1451704 1.2627861 -1.8852066 -0.8324722 4.130666 0.0788831 -2.3861298 3.3290750 2.4536828 -0.7881028 6.8388531 2.8891909 2.3766424 3.7569247 -2.4993799 1.846241 -1.8720644 3.6760059 -0.0793287 0.9553124 3.5302088 2.8229766 1.9054433 2.0903841 2.126011 -1.8324137 -2.5411060 2.2986315 4.5185081 2.6298880 -1.7034878 0.1103810 2.1441119 -2.3426218 -1.4942959 6.0660064 0.2770576 -4.2644216 4.4382470 -4.1272113 1.4978426 -4.3379426 -2.3860744 -2.743482 8.6801296 0.4912353 -3.4577271 -4.6427416 -6.0232588 -1.2000095 1.2915031 1.7547299 0.5286464 -3.7662894 4.7920103 1.9458703 0.2371975 -1.0947628 -0.3974943 -2.5085937 -0.5398389 0.2735771 1.5247094 -3.624901 2.3300559 -2.0270701 1.2641799 -0.9181785 9.4161516 4.1023113 -1.0459135
Sunfish 2.671976 -1.634745 1.0092547 1.971743 -0.0659806 -4.485483 -0.6157394 -0.4446398 -2.135539 -0.1855337 -0.3653675 -0.9757169 0.9833536 -0.4373506 -0.6857746 1.395239 3.526766 0.5293000 0.4209848 -3.4104846 -3.9299972 -0.4856075 1.1505664 2.247154 1.291273 0.4333016 0.5757962 0.7421109 1.3637568 0.4675282 -1.1996607 -1.1199203 -0.5715238 0.7483034 -0.2462562 1.295735 -2.1481787 2.2357764 -0.1000319 -1.334179 -0.4678468 0.6883993 0.7824726 1.986898 0.1823848 0.5900189 -0.6196374 -1.197890 -0.6397997 1.8739895 -0.6986206 0.3264926 -1.106825 -0.7986667 -0.7942869 0.3534946 1.5704717 1.099842 -0.4712961 -1.9296982 -1.2794992 -0.6377849 -0.4841883 -0.7619484 -1.848736 1.861377 -3.0369215 1.040914 -0.8948211 0.5964522 -2.1729557 -2.847560 0.3661112 0.7198424 -0.2592527 -1.005021 0.9121147 -1.991298 -4.0090396 0.9573294 0.837238 1.5041342 0.5979597 -1.329158 0.1356189 0.1137635 0.5712501 1.144023 -2.901016 1.7090371 0.1723399 0.7930372 -1.302795 -0.1576198 0.6288434 1.809818 0.9966588 0.1851948 -0.9818111 -0.5018535 0.6628568 0.2434079 0.3056738 -1.179871 1.0023197 1.9586813 -0.2391551 -0.5478282 0.4052252 0.1050347 0.0608304 0.6921589 0.090562 1.737157 -0.1224932 1.382547 -0.0669635 0.6275334 1.1357830 0.7212117 1.332176 -0.1708254 1.631695 1.046881 2.6477299 -0.7323800 0.2450768 1.0407605 -0.0638638 0.0362075 -0.7602249 -0.4514443 1.0970385 -2.058717 -0.4772066 0.6891971 -0.5252515 1.320807 2.0855522 -0.5308727 -0.5414442 0.8132779 0.8060741 -0.7820840 -0.0974855 -1.1516975 -0.4544318 -0.1437671 1.0149376 -0.7442971 0.2365992 0.5018176 -0.0623840 0.7175603 -1.4370608 2.1816251 -1.1740033 -0.1587521 -1.308936 1.0963224 0.9416279 0.6446517 -0.7079844 -1.3376836 0.8534419 -0.1928021 -0.4729082 -0.6972626 -0.595974 0.9460619 -1.1913831 0.1201412 0.0858613 -0.0684709 0.8437920 -0.956026 0.9757848 -0.0287358 0.2485583 0.2431551 -1.2416189 0.0169046 -0.5596322 0.2480171 -0.637270 -0.4266425 -0.8717179 2.0318199 0.2985858 -0.8279377 0.2846029 0.8176092 0.2886955 -1.0247478 1.3453494 -1.0061067 -0.0779308 -0.2040882 -0.9363782 0.6747263 1.141463 -1.2624069 -0.8103713 1.212891 0.0998399 0.9855938 0.7333348 -0.8901834 1.282947 -2.092871 -0.3857082 -0.4879458 0.3354572 0.5785333 -0.8967905 1.6569907 -0.3487997 -0.9604216 0.8301921 0.1801073 -2.2939194 -1.8618764 -0.5685490 -1.1015050 0.8148482 0.4927997 -1.7475145 2.6695286 -1.1713861 -1.5447938 -1.7871431 0.0911204 -0.0722668 -1.870259 -0.3034667 0.4476403 -2.0470996 0.2271746 -1.2681026 -0.3287547 -0.2580326 -0.7302849 -0.7232476 -0.1447716 1.142121 0.0259262 0.9386625 0.0660591 2.2029548 0.5374430 0.2409948 0.9285480 -0.3375535 -1.890040 0.5197283 0.5158098 0.5181227 0.6476375 1.0858086 0.2762813 -0.1479446 0.2331174 -0.1243531 0.7791777 -0.6929186 0.3517946 1.4635512 0.2301916 1.3337702 0.2979985 -0.4120448 -0.3105052 1.233750 -1.7594159 0.6044447 0.8377204 -1.4883090 -1.3467319 0.2149829 0.6559219 -0.8600177 0.6556712 -0.1650155 1.9970446 -0.4196055 0.4368345 -1.2926978 -0.8387044 -0.4728054 0.0600773 -0.8078840 -0.8101973 1.032789 0.0913317 -0.2479691 1.2400970 -0.2620524 -0.4653997 -0.6517328 0.2916859
Salmon 45.366639 -14.246932 0.1114514 19.613487 2.7339021 -37.359271 -15.1653864 -15.6177393 -28.997250 -3.1877566 -6.6190532 -15.9110693 -0.7991651 -1.2207791 25.7337867 18.073711 18.250084 10.8358750 23.4028315 -6.8574182 -28.0333926 -6.6615900 11.7924924 -2.439194 15.361486 2.6333636 -18.3236103 20.7517419 -38.2821673 -21.7261770 -8.9726795 -3.8014499 23.8414364 -10.6582933 -32.0685345 10.280295 0.1439898 -5.1115406 7.0460971 -12.619112 -15.9090216 5.6932943 0.0775648 6.647813 -15.5498536 -11.8216543 -9.0334169 -1.171663 3.2620234 11.2080167 -0.9551670 2.5643337 -17.841022 -7.5145103 -21.0402972 0.2247946 -2.1811709 6.188044 2.9438778 -5.0685389 -15.7523157 16.3098186 11.8173905 -9.4073700 -6.414177 10.251040 -12.0886915 3.541542 -10.0117884 1.7279323 -13.3995545 -24.430764 14.0453707 -10.6955862 8.0365074 -1.883903 12.6877520 -6.894571 -22.7189243 4.7472505 4.395364 -2.1284084 5.7227501 -2.650241 9.1897555 2.1506669 9.4490531 2.812082 -20.816413 8.5460037 0.2949648 0.7946690 -10.656108 9.9514356 -12.6217186 16.278604 0.8662642 2.8323808 5.5195939 0.5749495 -13.1863747 2.6751923 -13.9624171 -11.584639 -15.9321708 12.5074162 -3.9394292 -3.4320179 3.9509412 8.7203613 -0.9916812 4.4278251 -5.484492 5.542930 5.9595888 -1.846622 -2.2214505 -9.5353523 0.6262246 -2.1146753 6.978190 2.4015731 -2.978400 23.274266 10.1745679 -12.6547850 8.5752611 20.8520893 -4.7983872 3.8165092 1.6916782 -4.5252281 0.3917101 -9.628118 -10.0832436 16.4615853 -2.9438570 4.273337 9.3487661 -9.0246737 -8.1324818 9.3575397 -5.6498191 3.4268754 -7.4079411 -5.2582906 -3.8441174 5.5187426 2.6453734 -6.7113518 6.7072824 -7.0429745 3.1472481 -0.6532618 -5.3207453 0.4442541 -4.4238417 -8.6722421 -20.286674 16.6801413 6.2967667 12.9163128 2.5236117 -16.1339405 3.9601234 7.9584559 -3.5261174 -4.7358231 -6.021531 1.3814217 -9.8024166 5.5291360 5.4079389 -6.4259406 7.3904302 -6.307354 1.5624471 9.8609787 -3.2719547 -3.8165110 -6.0230060 2.2525619 -9.1581446 0.5150933 -3.109665 -12.3073288 0.1718954 8.2760865 1.0782718 -4.5277518 3.5575192 8.7853295 -7.3308001 -10.3578341 3.8029453 -0.7960860 -4.5135825 2.4909701 -5.2377565 2.8056909 4.187349 2.1199821 -14.5778237 -5.288184 0.7636763 1.0684937 11.4446640 -3.8809239 -1.471044 -5.762816 9.2540044 -3.1231925 3.6703762 -2.7950426 -7.2013671 4.9203092 -2.9570233 -5.5224954 5.8207086 1.3965562 -22.8025385 -9.1081463 -7.9102714 -4.8906529 -4.1812692 12.2521930 -21.5515863 11.7752264 -6.4152903 -4.5245436 -15.0166304 4.7185851 -2.0311982 -15.313263 1.9812127 3.7187549 -19.9333908 -2.4657861 -10.5151205 5.2766498 -0.7492269 -4.5585811 5.6919522 3.2917922 14.842447 1.6301066 2.8435282 1.9368151 10.4675541 -1.6156167 3.1438421 3.1220410 -7.2900231 -11.184279 7.0010860 6.1452208 1.4225875 9.5060338 7.1725843 10.3827746 -4.0648451 1.0313897 -2.1608972 5.0647489 4.5756690 7.6646325 14.0237157 5.6951812 9.2584501 4.4598100 -15.8358166 -3.1563305 1.917434 -17.5611094 17.6697035 1.3399241 -2.5608145 -11.7418063 3.3866112 4.1455907 -7.2857001 12.7853620 -0.8977347 9.5451436 -5.8540317 -1.8019472 -7.1901684 -7.3223563 10.1887605 -1.8333372 2.3858847 6.8776140 13.609219 3.4711277 2.3469568 12.4018618 -3.9063538 -5.5250340 -0.2188451 0.2033760

Of course, the raw numbers alone are not especially easy to interpret. The graph below may make it a bit easier to see how items in the same category have vector representations with similar “shapes” whereas items from different categories have different “shapes”. That said, since these vectors have 300 dimensions, I only plot the first 30 entries!

Code
toPlot <- c()
for (i in 1:nrow(lsa_reps_examples)) {
    toPlot <- rbind(
        toPlot,
        tibble(item = rownames(lsa_reps_examples)[i], index = 1:30, val = lsa_reps_examples[i,1:30])
    )
}

toPlot %>%
    mutate(item = factor(item, levels = concept_names)) %>%
    mutate(category = factor(item, levels = concept_names, labels = category_names)) %>%
    mutate(exemplar = factor(item, levels = concept_names, labels = rep(c("A", "B"), length(concept_names) / 2))) %>%
    ggplot(aes(x = index, y = val, color = item)) +
    geom_line() +
    facet_wrap("category")

Of course, this is all just based on our visual impressions—we will now explore different ways of operationalizing similarity between representations. We will then see how to incorporate similarity into models of behavior.

10.3 Similarity between distributed representations

As noted earlier, different kinds of representations enable different ways of operationalizing the similarity between those representations. This is important because similarity is a core construct in many cognitive models, like we saw with the EBRW. Indeed, the EBRW’s approach to similarity is the first we will consider.

10.3.1 Transformed distance

Recall that the EBRW operationalized similarity as an exponential function of distance between items. Specifically, the similarity \(s_{ij}\) between representations of items \(i\) and \(j\) was defined as \[ s_{ij} = \exp \left(-c d_{ij} \right) \] where \(c\) was a sensitivity parameter and \(d_{ij}\) is the distance between \(\mathbf{x_i}\), the vector representing item \(i\), and \(\mathbf{x_j}\), the vector representing item \(j\): \[ d_{ij} = \left( \sum_{k = 1}^D w_k \left| x_{ik} - x_{jk} \right|^p \right)^{\frac{1}{p}} \] where \(D\) is the number of dimensions in each representation, \(w_k\) is the weight given to dimension \(k\), and \(p\) is a new parameter that we just added for this chapter. The new parameter \(p\) makes the distance formula more general. When \(p = 2\), we get the Euclidean distance we used in the last chapter. But \(p\) can, in principle, be any nonnegative number; this more general distance formula is known as a (Minkowski distance)[https://en.wikipedia.org/wiki/Minkowski_distance].

The choice of \(p\) reflects how differences in different dimensions contribute to the overall distance. When \(p = 2\), corresponding to Euclidean distance, the distance (and therefore similarity) between representations only depends on how far apart their vector representations are, not their orientation relative to the dimensions of the space. Another common choice of \(p\) in cognitive models is \(p = 1\), corresponding to the “city block” or “taxicab” distance. When \(p = 1\), the distance is the sum of the absolute differences between vector representations. As a result, distance (and therefore similarity) depends on how the two items are oriented with respect to the dimensions of the space.

We can visualize the effect of different choices of \(p\) by drawing contours of equal distance. We can imagine assigning item \(i\) to have a vector representation of \((0, 0)\) and then consider the all the possible positions of item \(j\) that would result in a distance \(d_{ij} = 1\). That’s what is shown in the graph below:

Code
expand_grid(p = c(0.25, 0.5, 1, 2, 4, 8), theta = seq(0, 2*pi, length.out = 501)) %>%
    mutate(x = cos(theta)) %>%
    mutate(y = sign(sin(theta)) * (1 - abs(x)^p)^(1 / p)) %>%
    ggplot(aes(x = x, y = y, color = factor(p), group = p, linewidth = factor(p))) +
    geom_path() +
    coord_equal() +
    scale_linewidth_manual(values = c("0.25" = 0.5, "0.5" = 0.5, "1" = 1.5, "2" = 1.5, "4" = 0.5, "8" = 0.5)) +
    labs(x = expression(x[j1]), y = expression(x[j2]), color = "p", linewidth = "p")

When \(p = 2\), the contour of equal distance is a circle—distance is irrespective of the orientation relative to the two dimensions of the space. When \(p = 1\), the contour of equal distance is a diamond—distance is the sum of the differences on each dimension. As noted above, most cognitive models adopt either \(p = 1\) or \(p = 2\), but other values of \(p\) are entirely possible. When \(p\) gets really big, the contour of equal distance approaches a square—distance depends on the maximum difference. When \(p\) gets really small, the contour of equal distance approaches a “star”—distance depends on the minimum difference.

A major conceptual point to take from the preceding discussion about the Minkowski distance parameter \(p\) is that, whenever \(p \neq 2\), we need to take the dimensions of the space seriously. When \(p \neq 2\), distance depends on which dimensions exhibit which differences. The resulting distance is only interpretable if those dimensions are interpretable, in turn. As a result, this approach to similarity is best suited to separable distributed representations.

For similar reasons, attention weights \(w_k\) only make sense to apply to separable distributed representations. If the elements of a distributed representation cannot be interpreted as referring to different attributes/features, as with an integral distributed representation, it makes little sense to assign those elements different attention weights. Of course, there is nothing stopping you from building such a model—it just will not have a very clear cognitive interpretation.

To give a concrete example of how the transformed distance approach to similarity can be implemented in R, we can adapt some of the code we wrote for the EBRW. This example uses the size and color representations for the eight concepts used in the examples in the previous section.

Code
# List names of items and attributes
concept_names <- c("Pine", "Oak", "Rose", "Daisy", "Robin", "Canary", "Sunfish", "Salmon")
sep_attribute_names <- c("size", "hue", "chroma", "lightness")

# Define matrix of representations
separable_distributed_reps <- matrix(c(
    50, 131, 36, 27,
    40, 20, 60, 16,
    0.1, 3, 58, 79,
    0.2, 52, 100, 50,
    0.3, 9, 63, 38,
    0.1, 60, 100, 63,
    0.5, 32, 100, 59,
    1, 5, 99, 71
), nrow = 8, ncol = length(alt_attribute_names), byrow = TRUE, dimnames = list(concept_names, alt_attribute_names))

# The "c" parameter representing sensitivity
sensitivity <- 0.01

# The "p" parameter representing the Minkowski parameter
minkowski_p <- 1

# These are the weights given to each of the four dimensions.
attention_weight <- c(1, 1, 1, 1)
# By convention, these weights are constrained to sum to 1.
attention_weight <- attention_weight / sum(attention_weight)

# First, define an empty distance matrix
distance <- matrix(0, nrow = nrow(separable_distributed_reps), ncol = nrow(separable_distributed_reps), dimnames = list(concept_names, concept_names))

# Fill in each entry in the distance matrix 
for (i in 1:nrow(separable_distributed_reps)) {
    for (j in 1:nrow(separable_distributed_reps)) {
        distance[i, j] <- sum(attention_weight * abs(separable_distributed_reps[i,] - separable_distributed_reps[j,])^minkowski_p)^(1 / minkowski_p)
    }
}

similarity <- exp(-sensitivity * distance)

print(round(similarity, 2))
        Pine  Oak Rose Daisy Robin Canary Sunfish Salmon
Pine    1.00 0.68 0.53  0.58  0.59   0.58    0.54   0.49
Oak     0.68 1.00 0.74  0.69  0.83   0.66    0.71   0.69
Rose    0.53 0.74 1.00  0.74  0.88   0.75    0.80   0.88
Daisy   0.58 0.69 0.74  1.00  0.79   0.95    0.93   0.84
Robin   0.59 0.83 0.88  0.79  1.00   0.75    0.82   0.83
Canary  0.58 0.66 0.75  0.95  0.75   1.00    0.92   0.85
Sunfish 0.54 0.71 0.80  0.93  0.82   0.92    1.00   0.90
Salmon  0.49 0.69 0.88  0.84  0.83   0.85    0.90   1.00

Go ahead, try it out yourself! You may notice that the sensitivity parameter is pretty small, see what happens if you increase it. In fact, that will be an exercise for us later!

10.3.2 Dot product

The dot product or inner product is a way of directly computing the similarity between two vectors, without first computing a distance between them. While transformed distance is the most common approach to modeling similarity in the GCM/EBRW, dot products have been used a lot in other models of memory, notably the Theory Of Distributed Associative Memory (TODAM; Murdock (1982)).

The dot product between two vectors is sum of the products of their elements. To get formal about it, the dot product between vector representations \(\mathbf{x_i}\) and \(\mathbf{x_j}\) is often written as \(\mathbf{x_i} \cdot \mathbf{x_j}\) (can you guess why this is called a dot product?). The dot product is defined as: \[ \mathbf{x_i} \cdot \mathbf{x_j} = \sum_{k = 1}^D x_{ik} x_{jk} \]

Intuitively, the dot product quantifies the amount of overlap between two representations. Any time the two vectors have elements that are both large and both of the same sign, their dot product will be larger. That’s why the dot product is a useful way to operationalize similarity.

To make this concrete, let’s consider a pair of two-dimensional vector representations (so \(D = 2\) in this example). We will assume that \(x_{11} = x_{21} = 1\), i.e., that the first element of each vector representation equals 1. The second elements of each representation (\(x_{12}\) and \(x_{22}\)) will vary between -2 and 2. The colors in the graph below indicates the value of the dot product for each combination of values of $x_{21}The chunk of code below varies the elements of these vectors so you can see how their dot product is influenced by those changes.

Code
expand_grid(x11 = 1, x12 = seq(-2, 2, length.out = 11), x21 = 1, x22 = seq(-2, 2, length.out = 11)) %>%
    mutate(dot_product = x11 * x21 + x12 * x22) %>%
    ggplot(aes(x = x12, y = x22, fill = dot_product)) +
    geom_raster() +
    scale_fill_gradient2() +
    coord_equal() +
    labs(x = expression(x[12]), y = expression(x[22]), fill = expression(bold(x[1]) %.% bold(x[2])))

Notice that the dot product is large and positive (i.e., very blue) whenever \(x_{12}\) and \(x_{22}\) are large and of the same sign, which happens in the upper right and lower left quadrants of the graph above. To the extent that \(x_{12}\) and \(x_{22}\) are large but of different signs, the dot product becomes negative (the red regions in the upper left and lower right quadrants). The dot product thus quantifies the degree to which the elements of each representation have the same sign, weighted by the magnitude of the elements of each representation.

While dot products can be computed with continuous values, they can also be computed for binary values just as well. For any element that contains a zero in the vectors being compared, that element will contribute zero to the dot product. Therefore, the dot product between representations that consist of ones and zeros is the number of elements for which both representations contain a one.

For example, using the concept features from earlier, the following is a matrix where each entry gives the dot product between the representations of the items in the corresponding rows/columns.

Code
concept_names <- c("Pine", "Oak", "Rose", "Daisy", "Robin", "Canary", "Sunfish", "Salmon")

attribute_names <- c("can_grow", "is_living", "has_roots", "has_bark", "is_big", "has_branches", "is_green", "has_leaves", "has_petals", "is_pretty", "is_red", "is_yellow", "can_move", "has_feathers", "can_fly", "has_wings", "can_sing", "can_swim", "has_gills", "has_scales")

concept_attributes <- matrix(c(
    1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1
), nrow = 8, ncol = length(attribute_names), byrow = TRUE, dimnames = list(concept_names, attribute_names))

tcrossprod(concept_attributes)
        Pine Oak Rose Daisy Robin Canary Sunfish Salmon
Pine       7   6    3     3     2      2       2      2
Oak        6   7    4     4     2      2       2      2
Rose       3   4    7     6     3      2       2      3
Daisy      3   4    6     7     2      3       3      2
Robin      2   2    3     2     7      6       3      4
Canary     2   2    2     3     6      8       4      3
Sunfish    2   2    2     3     3      4       7      6
Salmon     2   2    3     2     4      3       6      7

It is worth taking a look at the code for this one, since R makes it easy to compute the dot products between many vectors simultaneously using the tcrossprod function, as illustrated in the final line of the code chunk above. R has two functions that implement particular forms of (matrix multiplication)[https://en.wikipedia.org/wiki/Matrix_multiplication], crossprod and tcrossprod. Specifically, tcrossprod returns the cross-product of an \(N \times M\) matrix \(A\) with the transpose of a \(K \times M\) matrix \(B\). The “transpose” of a matrix is what you get when you swap its rows and columns. So the transpose of matrix \(B\) has dimension \(M \times K\). The result is an \(N \times K\) matrix where the entry in the \(i\)th row and \(j\)th column is the dot product of the \(i\)th row of \(A\) and the \(j\)th row of \(B\). This is illustrated below.

Code
A <- matrix(rnorm(n = 15), nrow = 3, ncol = 5)
B <- matrix(rnorm(n = 20), nrow = 4, ncol = 5)

print(A)
          [,1]       [,2]       [,3]       [,4]       [,5]
[1,] -1.042958 -0.4140553  1.3762569 -1.2603923  0.1210855
[2,] -1.070853 -0.3916844 -0.9313825  0.7158927 -2.6305672
[3,]  0.379089  1.5611897  1.9640299  0.7494422 -0.8950186
Code
print(B)
           [,1]       [,2]       [,3]      [,4]       [,5]
[1,] -1.1570559 -0.1226359  0.7488086 -2.455725 0.80124750
[2,]  0.5232695  1.7441242 -0.8286818 -1.545715 0.05190826
[3,]  0.1259115 -1.7034857  1.8937240 -0.436462 0.24459708
[4,] -1.0263656 -1.2446303  1.0119758  1.201336 0.54842031
Code
tcrossprod(A, B)
          [,1]        [,2]       [,3]       [,4]
[1,]  5.480287 -0.45389825  3.7599981 1.53079186
[2,] -3.276126 -1.71478563 -2.1872745 0.06142366
[3,] -1.716958  0.08883569  0.5615761 0.06484820

When you call tcrossprod with just a single matrix, R uses that same matrix for both \(A\) and \(B\). For example:

Code
tcrossprod(A)
           [,1]       [,2]      [,3]
[1,]  4.7565358 -1.2236171 0.6082522
[2,] -1.2236171  9.6000030 0.0442214
[3,]  0.6082522  0.0442214 7.8011570
Code
tcrossprod(B)
           [,1]      [,2]      [,3]       [,4]
[1,]  8.5871127  2.397573  2.749072 -0.4127539
[2,]  2.3975730  6.394423 -3.787157 -5.3749163
[3,]  2.7490719 -3.787157  6.754235  3.5171862
[4,] -0.4127539 -5.374916  3.517186  5.3705980

So to get back to talking about dot products between vector representations, tcrossprod(concept_attributes) returns a matrix where the entry in the \(i\)th row and \(j\)th column is the dot product between the \(i\)th row of concept_attributes and the \(j\)th row of concept_attributes. Convenient!

As we saw, the dot product can be used to quantify similarity between separable distributed vector representations. It can also be used to quantify similarity between integral distributed vector representations. For example, this is the matrix of dot-product similarities between the integral representations of the concepts in our running example:

Code
knitr::kable(tcrossprod(lsa_reps_examples))
Pine Oak Rose Daisy Robin Canary Sunfish Salmon
Pine 38573.968 37331.6943 18080.1644 3954.26442 8252.5993 4161.7246 1012.64213 12580.369
Oak 37331.694 59365.5215 31061.6833 5111.06152 11119.6447 3805.2614 703.56230 14579.626
Rose 18080.164 31061.6833 83666.0058 9616.77767 22586.7398 6677.3640 544.37714 12069.413
Daisy 3954.264 5111.0615 9616.7777 3147.26980 5174.1907 1266.2499 92.93532 1729.109
Robin 8252.599 11119.6447 22586.7398 5174.19071 25216.0414 4542.8128 307.62160 4887.937
Canary 4161.725 3805.2614 6677.3640 1266.24994 4542.8128 6624.3994 277.54518 1760.961
Sunfish 1012.642 703.5623 544.3771 92.93532 307.6216 277.5452 405.72158 2192.350
Salmon 12580.369 14579.6264 12069.4125 1729.10943 4887.9368 1760.9615 2192.34977 33261.839

10.3.3 Cosine similarity

As noted above, the dot product is sensitive to the magnitude of the values in the vector representations. This could be important if magnitude represents something important in the context of our model. For example, just like the EBRW allows for a “strength” parameter that can multiplicatively scale the similarity between two representations, we could imagine that the magnitude of the entries in a vector represents the “strength” or “salience” of the item that vector represents.

In some models, though, the magnitudes are irrelevant—instead an item is represented by the pattern of relative values in the vector. We can still use the dot product to quantify similarity, but we will first need to normalize each vector representation. Specifically, we will normalize the vectors such that their dot products with themselves are equal to 1. Referring back to the definition of the dot product above, the dot product of a vector with itself is the sum of the squares of the entries in the vector. So if we divide each entry in a vector by the square root of the sum of the squared entries, we obtain our normalized vector representation.

We can see how this works by imagining that we have two integral distributed vector representations, which the code below randomly generates and then shows.

Code
x_1 <- rnorm(n = 10, mean = 0, sd = 1)
x_2 <- rnorm(n = 10, mean = 0, sd = 1)

knitr::kable(rbind(x_1, x_2))
x_1 1.4623004 -0.4582554 -1.3126406 -0.7356256 0.6091427 0.1927802 -0.0306855 0.5532889 0.7257781 1.088871
x_2 -0.9597082 0.6364972 0.4518392 -0.3443309 -0.1859800 -1.0846488 0.3763883 -1.6267809 0.6824552 -1.126632

To create normalized versions of these representations, we divide them by the square root of the sum of their squared entries:

Code
x_1_norm <- x_1 / sqrt(sum(x_1^2))
x_2_norm <- x_2 / sqrt(sum(x_2^2))

knitr::kable(rbind(x_1_norm, x_2_norm))
x_1_norm 0.5511184 -0.1727094 -0.4947139 -0.2772459 0.2295764 0.0726559 -0.0115649 0.2085260 0.2735345 0.4103787
x_2_norm -0.3532000 0.2342491 0.1662897 -0.1267236 -0.0684459 -0.3991817 0.1385216 -0.5987017 0.2511630 -0.4146329

To see the effect of this normalization on the dot products, let’s compute the dot products between the unnormalized vectors and for the normalized ones:

Code
# Dot product between unnormalized x_1 and itself
sum(x_1 * x_1)
[1] 7.040174
Code
# Dot product between unnormalized x_2 and itself
sum(x_2 * x_2)
[1] 7.383073
Code
# Dot product between unnormalized x_1 and x_2
sum(x_1 * x_2)
[1] -4.000327
Code
# Dot product between normalized x_1 and itself
sum(x_1_norm * x_1_norm)
[1] 1
Code
# Dot product between normalized x_2 and itself
sum(x_2_norm * x_2_norm)
[1] 1
Code
# Dot product between normalized x_1 and x_2
sum(x_1_norm * x_2_norm)
[1] -0.5548623

By normalizing the vectors so that the dot product with themselves is 1, we can treat the result as a measure of relative similarity between vector representations. Since similarity is greatest between a representation and itself, the dot product between normalized vector representations has an upper bound of 1. The dot product also has a lower bound of -1, which is achieved when the two vectors have exactly opposite entries:

Code
sum(x_1_norm * (-x_1_norm))
[1] -1

Just for completeness, let’s see how we can use the tcrossprod trick in this example:

Code
# Matrix of dot products for unnormalized representations
tcrossprod(rbind(x_1, x_2))
          x_1       x_2
x_1  7.040174 -4.000327
x_2 -4.000327  7.383073
Code
# Matrix of dot products for normalized representations
tcrossprod(rbind(x_1_norm, x_2_norm))
           x_1_norm   x_2_norm
x_1_norm  1.0000000 -0.5548623
x_2_norm -0.5548623  1.0000000

This might remind you of something—the Pearson correlation coefficient, which also ranges between 1 and -1 and measures the degree of correspondence between two set of numbers. Indeed, the normalized dot product can be interpreted in exactly the same way as the correlation coefficient! They are not strictly identical, however, as the correlation coefficient involves subtracting out the mean of each set of numbers, which we do not do to get the normalized dot product.

The normalized dot product has another, potentially even more evocative interpretation: It is the cosine of the angle between two vector representations. Specifically, we can treat a vector representation as specifying the end point of a line segment that starts at the origin (i.e., the point where all vector elements are equal to zero). The line segment thus goes in a particular direction from the origin. The normalized dot product between two vectors is the cosine of the angle between the directions specified by each vector. For that reason, the normalized dot product is often called cosine similarity.

The basic idea of cosine similarity is illustrated in the graph below. The graph shows different vector representations \(\mathbf{x_j}\) that are at different angles \(\theta_{ij}\) relative to another vector representation \(\mathbf{x_i}\). When the two vectors point generally in the same direction (to the left), the cosine similarity \(s_{ij} = \cos \left( \theta_{ij} \right)\) is greater than zero. When the two vectors point generally in the opposite direction (i.e., when \(\mathbf{x_j}\) points more to the right than the left), cosine similarity is less than zero. Finally, when the two vectors are orthogonal, cosine similarity is zero.

Code
plot_angles <- seq(0, 2 * pi - pi / 6, by = pi / 6)
angle_ticks <- seq(0, pi, by = pi / 4)
angle_labels <- c(expression(0), expression(pi / 4), expression(pi / 2), expression(3 * pi / 4), expression(pi))

circPlotDF <- tibble(x = 0, y = 0, xend = -cos(plot_angles), yend = sin(plot_angles), theta = acos(cos(plot_angles)))

circPlot <- circPlotDF %>%
    ggplot(aes(x = x, y = y, xend = xend, yend = yend, color = theta)) +
    geom_segment(arrow = arrow(length = unit(0.04, "npc"))) +
    annotate(geom = "segment", x = 0, y = 0, xend = -1, yend = 0, arrow = arrow(length = unit(0.04, "npc"), type = "closed"), color = "black", linewidth = 1) +
    annotate(geom = "curve", x = -0.7, y = 0, xend = -0.7 * cos(plot_angles[2]), yend = 0.7 * sin(plot_angles[2]), linetype = "dashed", color = "black", curvature = -1/6) +
    annotate(geom = "text", x = -1.05, y = 0, label = "bold(x[i])", parse = TRUE, hjust = 1, vjust = 0.5, color = "black") +
    annotate(geom = "text", x = -1.05 * cos(plot_angles[2]), y = 1.05 * sin(plot_angles[2]), label = "bold(x[j])", parse = TRUE, hjust = 1, vjust = 0.5, color = color("sunset")(length(plot_angles))[2]) +
    annotate(geom = "text", x = -0.75 * cos(plot_angles[2] / 2), y = 0.75 * sin(plot_angles[2] / 2), label = "theta[ij]", parse = TRUE, hjust = 1, vjust = 0.5) +
    scale_color_sunset(range = c(0, 1), midpoint = pi / 2, guide = "none") +
    coord_equal(xlim = c(-1.1, 1.1), ylim = c(-1.1, 1.1)) +
    theme_void()

cosPlotDF <- tibble(theta = seq(0, pi, length.out = 101), s = cos(theta))

cosPlot <- cosPlotDF %>%
    ggplot(aes(x = theta, y = s, color = theta)) +
    geom_line() +
    geom_hline(yintercept = 0, linetype = "dashed", color = "black") +
    scale_color_sunset(range = c(0, 1), midpoint = pi / 2, guide = "none") +
    labs(x = expression(theta[ij]), y = expression(s[ij] == plain(cos)*(theta[ij]))) +
    scale_x_continuous(breaks = angle_ticks, labels = angle_labels)

print(circPlot + cosPlot + plot_layout(ncol = 1))

Cosine similarity between vector representations \(\mathbf{x_i}\) and \(\mathbf{x_j}\) can be formally specified in terms of the entries in each vector, without needing to explicitly specify the angle \(\theta_{ij}\): \[ s_{ij} = \cos \left( \theta_{ij} \right) = \frac{\sum_{k = 1}^D x_{ik} x_{jk}}{\left( \sqrt{\sum_{k = 1}^D x_{ik}^2} \right) \left( \sqrt{\sum_{k = 1}^D x_{jk}^2} \right)} \] where the numerator is the dot product and the denominator is the result of normalizing each vector.

Above, we saw how to use the tcrossprod trick to quickly compute the dot products between a set of vector representations that are stored in a matrix, with one row per item. We can use the same trick to compute the cosine similarities between those vectors if we first normalize them. One way to do that is to use a for loop to create a new matrix of normalized representations, like we did with x_1 and x_2 above. The code below does this with lsa_reps_examples, the integral distributed representations for our eight living things. It then uses tcrossprod with the normalized representations (reps_normalized) to get the cosine similarities for each pair of items.

Code
reps_normalized <- lsa_reps_examples

for (i in 1:nrow(reps_normalized)) {
    reps_normalized[i,] <- lsa_reps_examples[i,] / sqrt(sum(lsa_reps_examples[i,]^2))
}

knitr::kable(tcrossprod(reps_normalized))
Pine Oak Rose Daisy Robin Canary Sunfish Salmon
Pine 1.0000000 0.7801236 0.3182590 0.3588819 0.2646093 0.2603472 0.2559734 0.3512150
Oak 0.7801236 1.0000000 0.4407409 0.3739185 0.2873990 0.1918864 0.1433578 0.3280999
Rose 0.3182590 0.4407409 1.0000000 0.5926361 0.4917457 0.2836333 0.0934354 0.2287908
Daisy 0.3588819 0.3739185 0.5926361 1.0000000 0.5808139 0.2773187 0.0822431 0.1689983
Robin 0.2646093 0.2873990 0.4917457 0.5808139 1.0000000 0.3514901 0.0961755 0.1687773
Canary 0.2603472 0.1918864 0.2836333 0.2773187 0.3514901 1.0000000 0.1692959 0.1186324
Sunfish 0.2559734 0.1433578 0.0934354 0.0822431 0.0961755 0.1692959 1.0000000 0.5967915
Salmon 0.3512150 0.3280999 0.2287908 0.1689983 0.1687773 0.1186324 0.5967915 1.0000000

We can also get a bit fancy and use some more matrix algebra to do the normalization without using a for loop. Admittedly, unless you are used to linear algebra, this may not be too intuitive. However, the main idea is to multiply the matrix of representations by a diagonal matrix that normalizes each row:

Code
reps_normalized <- diag(1 / sqrt(rowSums(lsa_reps_examples^2))) %*% lsa_reps_examples

knitr::kable(tcrossprod(reps_normalized))
1.0000000 0.7801236 0.3182590 0.3588819 0.2646093 0.2603472 0.2559734 0.3512150
0.7801236 1.0000000 0.4407409 0.3739185 0.2873990 0.1918864 0.1433578 0.3280999
0.3182590 0.4407409 1.0000000 0.5926361 0.4917457 0.2836333 0.0934354 0.2287908
0.3588819 0.3739185 0.5926361 1.0000000 0.5808139 0.2773187 0.0822431 0.1689983
0.2646093 0.2873990 0.4917457 0.5808139 1.0000000 0.3514901 0.0961755 0.1687773
0.2603472 0.1918864 0.2836333 0.2773187 0.3514901 1.0000000 0.1692959 0.1186324
0.2559734 0.1433578 0.0934354 0.0822431 0.0961755 0.1692959 1.0000000 0.5967915
0.3512150 0.3280999 0.2287908 0.1689983 0.1687773 0.1186324 0.5967915 1.0000000

As you can see, the result is the same set of cosine similarities. The linear algebra approach can be more efficient than the for loop if we have a large number of representations to deal with, but for the purposes of this class I recommend whichever approach you are most comfortable with.

Finally, while cosine similarity can be computed using either separable or integral distributed representations, it makes a bit more sense to apply with integral distributed representations. That’s because the cosine similarity is a measure of the extent to which the entries in two vector representations exhibit the same patterns. This is a “holistic” measure of overall similarity which does not depend on the entries in each vector having any particular interpretation.

10.4 Modeling choice and response time with similarity

In the last chapter, we saw how the EBRW used similarity between representations of items to explain choice and response time in recognition tasks. The previous application did not include attention weights and only allowed for transformed distance as a measure of similarity. The chunk of code below shows can we can modify our EBRW code to accommodate different types of similarity and to allow for attention weights on transformed distances. This function is embedded in a simplified working example in this script. The changes are explained in comments.

Code
# To be more general, the function now asks for "stim_reps", which is a matrix of vector representations of each stimulus.  These could be any kind of vector representation (localist, separable, integral).
# The type of similarity to use is specified by the `sim_type` argument.  This can be "transformed_distance", "cosine", or "dot_product".
extended_ebrw_nll <- function(par, stim_reps, study_items, probe_item, response, rt, n_sims = 0, sim_type = "transformed_distance") {
    if (sim_type == "transformed_distance") {
        # If transformed distance similarity is specified, the parameter vector can also optionally specify parameters for *attention weights*.
        # If there are D columns in stim_reps, there are D - 1 free attention weights, since they must sum to one.
        # The attention weight parameters need only be nonnegative numbers, since they will be normalized anyway.
        if (is.na(par["attention_weight[1]"])) {
            attention_weight <- rep(1, ncol(stim_reps))
        } else {
            attention_weight <- c(par[paste0("attention_weight[", 1:(ncol(stim_reps) - 1), "]")], 1)
        }
        attention_weight <- attention_weight / sum(attention_weight)
        
        # You can also optionally specify a Minkowski parameter, but it will be assumed to be 2 if you don't
        if (is.na(par["minkowski_p"])) {
            minkowski_p <- 2
        } else {
            minkowski_p <- par["minkowski_p"]
        }
        
        # Finally, compute the matrix of stimulus similarities
        stim_sims <- diag(nrow(stim_reps))
        
        for (i in 2:nrow(stim_reps)) {
            for (j in 1:(i - 1)) {
                stim_sims[i, j] <- stim_sims[j, i] <- exp(-par["specificity"] * sum(attention_weight * abs(stim_reps[i,] - stim_reps[j,])^minkowski_p)^(1 / minkowski_p))
            }
        }
    } else {
        # If similarity is *not* transformed distance, then dot product is assumed.
        if (sim_type == "cosine") {
            # If cosine similarity is specified, then first normalize the representations.
            stim_reps <- diag(1 / sqrt(rowSums(stim_reps^2))) %*% stim_reps
        }
        
        # Finally, compute the matrix of stimulus similarities
        stim_sims <- tcrossprod(stim_reps)^par["specificity"]
    }
    
    evidence_mean <- rep(0, length(probe_item))
    evidence_sd <- rep(0, length(probe_item))
    
    for (i in 1:length(probe_item)) {
        summed_sim <- sum(stim_sims[probe_item[i], study_items[i,]], na.rm = TRUE)
        p <- summed_sim / (summed_sim + par["criterion"])
        
        evidence_mean[i] <- par["retrieval_rate"] * (2 * p - 1)
        evidence_sd[i] <- 2 * sqrt(par["retrieval_rate"] * p * (1 - p))
    }
    
    if (is.infinite(n_sims)) {
        # 2a. Compute predicted response probabilities and mean RT's for each trial
        result <- c()
        
        evidence_var <- evidence_sd^2
        
        for (i in 1:length(probe_item)) {
            trial_p_yes <- (1 - exp(-2 * par["a"] * evidence_mean[i] * par["w"] / evidence_var[i])) / (1 - exp(-2 * par["a"] * evidence_mean[i] / evidence_var[i]))
            trial_mean_rt_yes <- par["t0"] + (par["a"] / evidence_mean[i]) * (1 / tanh(par["a"] * evidence_mean[i] / evidence_var[i]) - par["w"] / tanh(par["a"] * evidence_mean[i] * par["w"] / evidence_var[i]))
            trial_mean_rt_no <- par["t0"] + (par["a"] / evidence_mean[i]) * (1 / tanh(par["a"] * evidence_mean[i] / evidence_var[i]) - (1 - par["w"]) / tanh(par["a"] * evidence_mean[i] * (1 - par["w"]) / evidence_var[i]))
            
            result <- rbind(
                result,
                tibble(trial = i, p_yes = trial_p_yes, mean_rt_yes = trial_mean_rt_yes, mean_rt_no = trial_mean_rt_no)
            )
        }
        
        # 3a. Return final result
        return(result)
    } else if (n_sims > 0) {
        # 2b. Simulate a response for each trial
        result <- c()
        
        # For each trial...
        for (i in 1:length(probe_item)) {
            # Simulate `n_sims` replications of this trial, using its corresponding parameters
            trial_sim <- sampWiener(
                N = n_sims,
                a = par["a"] / evidence_sd[i],
                w = par["w"],
                v = evidence_mean[i] / evidence_sd[i],
                t0 = par["t0"]
            )
            
            # Append the new set of simulations to our `result`, including an indicator of which trial we are simulating and an index for each simulation
            result <- rbind(
                result,
                tibble(trial = i, sim_index = 1:n_sims, rt = trial_sim$q, response = trial_sim$response)
            )
        }
        
        # 3b. Return final result
        return(result)
    } else {
        # 2c. Calculate the log-likelihood of each observed response/RT on each trial
        result <- try(WienerPDF(
            t = rt,
            response = response,
            a = par["a"] / evidence_sd,
            w = par["w"],
            v = evidence_mean / evidence_sd,
            t0 = par["t0"]
        ))
        
        # 3c. Return final result
        if (class(result) == "try-error") {
            return(Inf)
        } else {
            return(-sum(result$logvalue))
        }
    }
}

10.5 Exercises

  1. What kinds of items do you think are best modeled using separable representations and what kinds of items do you think are best modeled using integral representations? Are there particular characteristics of items that make separable or integral representations more appropriate?