We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
MAP and MRR wrong for multiple gold documents
**Describe the bug**
Both the MAP and the MRR show wrong values. It seems we calculate the score for single gold documents and then override it after each instead of calculating it for the whole batch of gold documents. 

**Expected behavior**
correct values

**To Reproduce**
```
retrieved_docs = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]
gold_docs = ["one", "two", "three","four","seven"]

from haystack.components.evaluators import DocumentMAPEvaluator, DocumentMRREvaluator
from haystack import Document

mapevaluator = DocumentMAPEvaluator()
mrrevaluator = DocumentMRREvaluator()
mapresult = mapevaluator.run(
    ground_truth_documents=[[Document(content=content) for content in gold_docs]],
    retrieved_documents=[[Document(content=content) for content in retrieved_docs]])

mrrresult = mrrevaluator.run(
    ground_truth_documents=[[Document(content=content) for content in gold_docs]],
    retrieved_documents=[[Document(content=content) for content in retrieved_docs]])

print(mapresult["individual_scores"])
print(mrrresult["individual_scores"])
print(mapresult["score"])
print(mrrresult["score"])
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `haystack/components/evaluators/document_mrr.py`
Content:
```
1 # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2 #
3 # SPDX-License-Identifier: Apache-2.0
4 
5 from typing import Any, Dict, List
6 
7 from haystack import Document, component
8 
9 
10 @component
11 class DocumentMRREvaluator:
12     """
13     Evaluator that calculates the mean reciprocal rank of the retrieved documents.
14 
15     MRR measures how high the first retrieved document is ranked.
16     Each question can have multiple ground truth documents and multiple retrieved documents.
17 
18     `DocumentMRREvaluator` doesn't normalize its inputs, the `DocumentCleaner` component
19     should be used to clean and normalize the documents before passing them to this evaluator.
20 
21     Usage example:
22     ```python
23     from haystack import Document
24     from haystack.components.evaluators import DocumentMRREvaluator
25 
26     evaluator = DocumentMRREvaluator()
27     result = evaluator.run(
28         ground_truth_documents=[
29             [Document(content="France")],
30             [Document(content="9th century"), Document(content="9th")],
31         ],
32         retrieved_documents=[
33             [Document(content="France")],
34             [Document(content="9th century"), Document(content="10th century"), Document(content="9th")],
35         ],
36     )
37     print(result["individual_scores"])
38     # [1.0, 1.0]
39     print(result["score"])
40     # 1.0
41     ```
42     """
43 
44     @component.output_types(score=float, individual_scores=List[float])
45     def run(
46         self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]
47     ) -> Dict[str, Any]:
48         """
49         Run the DocumentMRREvaluator on the given inputs.
50 
51         `ground_truth_documents` and `retrieved_documents` must have the same length.
52 
53         :param ground_truth_documents:
54             A list of expected documents for each question.
55         :param retrieved_documents:
56             A list of retrieved documents for each question.
57         :returns:
58             A dictionary with the following outputs:
59             - `score` - The average of calculated scores.
60             - `individual_scores` - A list of numbers from 0.0 to 1.0 that represents how high the first retrieved document is ranked.
61         """
62         if len(ground_truth_documents) != len(retrieved_documents):
63             msg = "The length of ground_truth_documents and retrieved_documents must be the same."
64             raise ValueError(msg)
65 
66         individual_scores = []
67 
68         for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):
69             score = 0.0
70             for ground_document in ground_truth:
71                 if ground_document.content is None:
72                     continue
73 
74                 for rank, retrieved_document in enumerate(retrieved):
75                     if retrieved_document.content is None:
76                         continue
77 
78                     if ground_document.content in retrieved_document.content:
79                         score = 1 / (rank + 1)
80                         break
81             individual_scores.append(score)
82 
83         score = sum(individual_scores) / len(retrieved_documents)
84 
85         return {"score": score, "individual_scores": individual_scores}
86
```

Path: `haystack/components/evaluators/document_map.py`
Content:
```
1 # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2 #
3 # SPDX-License-Identifier: Apache-2.0
4 
5 from typing import Any, Dict, List
6 
7 from haystack import Document, component
8 
9 
10 @component
11 class DocumentMAPEvaluator:
12     """
13     A Mean Average Precision (MAP) evaluator for documents.
14 
15     Evaluator that calculates the mean average precision of the retrieved documents, a metric
16     that measures how high retrieved documents are ranked.
17     Each question can have multiple ground truth documents and multiple retrieved documents.
18 
19     `DocumentMAPEvaluator` doesn't normalize its inputs, the `DocumentCleaner` component
20     should be used to clean and normalize the documents before passing them to this evaluator.
21 
22     Usage example:
23     ```python
24     from haystack import Document
25     from haystack.components.evaluators import DocumentMAPEvaluator
26 
27     evaluator = DocumentMAPEvaluator()
28     result = evaluator.run(
29         ground_truth_documents=[
30             [Document(content="France")],
31             [Document(content="9th century"), Document(content="9th")],
32         ],
33         retrieved_documents=[
34             [Document(content="France")],
35             [Document(content="9th century"), Document(content="10th century"), Document(content="9th")],
36         ],
37     )
38 
39     print(result["individual_scores"])
40     # [1.0, 0.8333333333333333]
41     print(result["score"])
42     # 0.9166666666666666
43     ```
44     """
45 
46     @component.output_types(score=float, individual_scores=List[float])
47     def run(
48         self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]
49     ) -> Dict[str, Any]:
50         """
51         Run the DocumentMAPEvaluator on the given inputs.
52 
53         All lists must have the same length.
54 
55         :param ground_truth_documents:
56             A list of expected documents for each question.
57         :param retrieved_documents:
58             A list of retrieved documents for each question.
59         :returns:
60             A dictionary with the following outputs:
61             - `score` - The average of calculated scores.
62             - `individual_scores` - A list of numbers from 0.0 to 1.0 that represents how high retrieved documents are ranked.
63         """
64         if len(ground_truth_documents) != len(retrieved_documents):
65             msg = "The length of ground_truth_documents and retrieved_documents must be the same."
66             raise ValueError(msg)
67 
68         individual_scores = []
69 
70         for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):
71             score = 0.0
72             for ground_document in ground_truth:
73                 if ground_document.content is None:
74                     continue
75 
76                 average_precision = 0.0
77                 relevant_documents = 0
78 
79                 for rank, retrieved_document in enumerate(retrieved):
80                     if retrieved_document.content is None:
81                         continue
82 
83                     if ground_document.content in retrieved_document.content:
84                         relevant_documents += 1
85                         average_precision += relevant_documents / (rank + 1)
86                 if relevant_documents > 0:
87                     score = average_precision / relevant_documents
88             individual_scores.append(score)
89 
90         score = sum(individual_scores) / len(retrieved_documents)
91 
92         return {"score": score, "individual_scores": individual_scores}
93
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/haystack/components/evaluators/document_map.py b/haystack/components/evaluators/document_map.py
--- a/haystack/components/evaluators/document_map.py
+++ b/haystack/components/evaluators/document_map.py
@@ -43,6 +43,7 @@
     ```
     """
 
+    # Refer to https://www.pinecone.io/learn/offline-evaluation/ for the algorithm.
     @component.output_types(score=float, individual_scores=List[float])
     def run(
         self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]
@@ -68,25 +69,21 @@
         individual_scores = []
 
         for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):
-            score = 0.0
-            for ground_document in ground_truth:
-                if ground_document.content is None:
-                    continue
-
-                average_precision = 0.0
-                relevant_documents = 0
+            average_precision = 0.0
+            average_precision_numerator = 0.0
+            relevant_documents = 0
 
-                for rank, retrieved_document in enumerate(retrieved):
-                    if retrieved_document.content is None:
-                        continue
-
-                    if ground_document.content in retrieved_document.content:
-                        relevant_documents += 1
-                        average_precision += relevant_documents / (rank + 1)
-                if relevant_documents > 0:
-                    score = average_precision / relevant_documents
-            individual_scores.append(score)
+            ground_truth_contents = [doc.content for doc in ground_truth if doc.content is not None]
+            for rank, retrieved_document in enumerate(retrieved):
+                if retrieved_document.content is None:
+                    continue
 
-        score = sum(individual_scores) / len(retrieved_documents)
+                if retrieved_document.content in ground_truth_contents:
+                    relevant_documents += 1
+                    average_precision_numerator += relevant_documents / (rank + 1)
+            if relevant_documents > 0:
+                average_precision = average_precision_numerator / relevant_documents
+            individual_scores.append(average_precision)
 
+        score = sum(individual_scores) / len(ground_truth_documents)
         return {"score": score, "individual_scores": individual_scores}
diff --git a/haystack/components/evaluators/document_mrr.py b/haystack/components/evaluators/document_mrr.py
--- a/haystack/components/evaluators/document_mrr.py
+++ b/haystack/components/evaluators/document_mrr.py
@@ -41,6 +41,7 @@
     ```
     """
 
+    # Refer to https://www.pinecone.io/learn/offline-evaluation/ for the algorithm.
     @component.output_types(score=float, individual_scores=List[float])
     def run(
         self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]
@@ -66,20 +67,17 @@
         individual_scores = []
 
         for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):
-            score = 0.0
-            for ground_document in ground_truth:
-                if ground_document.content is None:
-                    continue
-
-                for rank, retrieved_document in enumerate(retrieved):
-                    if retrieved_document.content is None:
-                        continue
+            reciprocal_rank = 0.0
 
-                    if ground_document.content in retrieved_document.content:
-                        score = 1 / (rank + 1)
-                        break
-            individual_scores.append(score)
+            ground_truth_contents = [doc.content for doc in ground_truth if doc.content is not None]
+            for rank, retrieved_document in enumerate(retrieved):
+                if retrieved_document.content is None:
+                    continue
+                if retrieved_document.content in ground_truth_contents:
+                    reciprocal_rank = 1 / (rank + 1)
+                    break
+            individual_scores.append(reciprocal_rank)
 
-        score = sum(individual_scores) / len(retrieved_documents)
+        score = sum(individual_scores) / len(ground_truth_documents)
 
         return {"score": score, "individual_scores": individual_scores}

{"golden_diff": "diff --git a/haystack/components/evaluators/document_map.py b/haystack/components/evaluators/document_map.py\n--- a/haystack/components/evaluators/document_map.py\n+++ b/haystack/components/evaluators/document_map.py\n@@ -43,6 +43,7 @@\n     ```\n     \"\"\"\n \n+    # Refer to https://www.pinecone.io/learn/offline-evaluation/ for the algorithm.\n     @component.output_types(score=float, individual_scores=List[float])\n     def run(\n         self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]\n@@ -68,25 +69,21 @@\n         individual_scores = []\n \n         for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):\n-            score = 0.0\n-            for ground_document in ground_truth:\n-                if ground_document.content is None:\n-                    continue\n-\n-                average_precision = 0.0\n-                relevant_documents = 0\n+            average_precision = 0.0\n+            average_precision_numerator = 0.0\n+            relevant_documents = 0\n \n-                for rank, retrieved_document in enumerate(retrieved):\n-                    if retrieved_document.content is None:\n-                        continue\n-\n-                    if ground_document.content in retrieved_document.content:\n-                        relevant_documents += 1\n-                        average_precision += relevant_documents / (rank + 1)\n-                if relevant_documents > 0:\n-                    score = average_precision / relevant_documents\n-            individual_scores.append(score)\n+            ground_truth_contents = [doc.content for doc in ground_truth if doc.content is not None]\n+            for rank, retrieved_document in enumerate(retrieved):\n+                if retrieved_document.content is None:\n+                    continue\n \n-        score = sum(individual_scores) / len(retrieved_documents)\n+                if retrieved_document.content in ground_truth_contents:\n+                    relevant_documents += 1\n+                    average_precision_numerator += relevant_documents / (rank + 1)\n+            if relevant_documents > 0:\n+                average_precision = average_precision_numerator / relevant_documents\n+            individual_scores.append(average_precision)\n \n+        score = sum(individual_scores) / len(ground_truth_documents)\n         return {\"score\": score, \"individual_scores\": individual_scores}\ndiff --git a/haystack/components/evaluators/document_mrr.py b/haystack/components/evaluators/document_mrr.py\n--- a/haystack/components/evaluators/document_mrr.py\n+++ b/haystack/components/evaluators/document_mrr.py\n@@ -41,6 +41,7 @@\n     ```\n     \"\"\"\n \n+    # Refer to https://www.pinecone.io/learn/offline-evaluation/ for the algorithm.\n     @component.output_types(score=float, individual_scores=List[float])\n     def run(\n         self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]\n@@ -66,20 +67,17 @@\n         individual_scores = []\n \n         for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):\n-            score = 0.0\n-            for ground_document in ground_truth:\n-                if ground_document.content is None:\n-                    continue\n-\n-                for rank, retrieved_document in enumerate(retrieved):\n-                    if retrieved_document.content is None:\n-                        continue\n+            reciprocal_rank = 0.0\n \n-                    if ground_document.content in retrieved_document.content:\n-                        score = 1 / (rank + 1)\n-                        break\n-            individual_scores.append(score)\n+            ground_truth_contents = [doc.content for doc in ground_truth if doc.content is not None]\n+            for rank, retrieved_document in enumerate(retrieved):\n+                if retrieved_document.content is None:\n+                    continue\n+                if retrieved_document.content in ground_truth_contents:\n+                    reciprocal_rank = 1 / (rank + 1)\n+                    break\n+            individual_scores.append(reciprocal_rank)\n \n-        score = sum(individual_scores) / len(retrieved_documents)\n+        score = sum(individual_scores) / len(ground_truth_documents)\n \n         return {\"score\": score, \"individual_scores\": individual_scores}\n", "issue": "MAP and MRR wrong for multiple gold documents\n**Describe the bug**\r\nBoth the MAP and the MRR show wrong values. It seems we calculate the score for single gold documents and then override it after each instead of calculating it for the whole batch of gold documents. \r\n\r\n**Expected behavior**\r\ncorrect values\r\n\r\n**To Reproduce**\r\n```\r\nretrieved_docs = [\"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"ten\"]\r\ngold_docs = [\"one\", \"two\", \"three\",\"four\",\"seven\"]\r\n\r\nfrom haystack.components.evaluators import DocumentMAPEvaluator, DocumentMRREvaluator\r\nfrom haystack import Document\r\n\r\nmapevaluator = DocumentMAPEvaluator()\r\nmrrevaluator = DocumentMRREvaluator()\r\nmapresult = mapevaluator.run(\r\n    ground_truth_documents=[[Document(content=content) for content in gold_docs]],\r\n    retrieved_documents=[[Document(content=content) for content in retrieved_docs]])\r\n\r\nmrrresult = mrrevaluator.run(\r\n    ground_truth_documents=[[Document(content=content) for content in gold_docs]],\r\n    retrieved_documents=[[Document(content=content) for content in retrieved_docs]])\r\n\r\nprint(mapresult[\"individual_scores\"])\r\nprint(mrrresult[\"individual_scores\"])\r\nprint(mapresult[\"score\"])\r\nprint(mrrresult[\"score\"])\r\n```\r\n\n", "before_files": [{"content": "# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>\n#\n# SPDX-License-Identifier: Apache-2.0\n\nfrom typing import Any, Dict, List\n\nfrom haystack import Document, component\n\n\n@component\nclass DocumentMRREvaluator:\n    \"\"\"\n    Evaluator that calculates the mean reciprocal rank of the retrieved documents.\n\n    MRR measures how high the first retrieved document is ranked.\n    Each question can have multiple ground truth documents and multiple retrieved documents.\n\n    `DocumentMRREvaluator` doesn't normalize its inputs, the `DocumentCleaner` component\n    should be used to clean and normalize the documents before passing them to this evaluator.\n\n    Usage example:\n    ```python\n    from haystack import Document\n    from haystack.components.evaluators import DocumentMRREvaluator\n\n    evaluator = DocumentMRREvaluator()\n    result = evaluator.run(\n        ground_truth_documents=[\n            [Document(content=\"France\")],\n            [Document(content=\"9th century\"), Document(content=\"9th\")],\n        ],\n        retrieved_documents=[\n            [Document(content=\"France\")],\n            [Document(content=\"9th century\"), Document(content=\"10th century\"), Document(content=\"9th\")],\n        ],\n    )\n    print(result[\"individual_scores\"])\n    # [1.0, 1.0]\n    print(result[\"score\"])\n    # 1.0\n    ```\n    \"\"\"\n\n    @component.output_types(score=float, individual_scores=List[float])\n    def run(\n        self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]\n    ) -> Dict[str, Any]:\n        \"\"\"\n        Run the DocumentMRREvaluator on the given inputs.\n\n        `ground_truth_documents` and `retrieved_documents` must have the same length.\n\n        :param ground_truth_documents:\n            A list of expected documents for each question.\n        :param retrieved_documents:\n            A list of retrieved documents for each question.\n        :returns:\n            A dictionary with the following outputs:\n            - `score` - The average of calculated scores.\n            - `individual_scores` - A list of numbers from 0.0 to 1.0 that represents how high the first retrieved document is ranked.\n        \"\"\"\n        if len(ground_truth_documents) != len(retrieved_documents):\n            msg = \"The length of ground_truth_documents and retrieved_documents must be the same.\"\n            raise ValueError(msg)\n\n        individual_scores = []\n\n        for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):\n            score = 0.0\n            for ground_document in ground_truth:\n                if ground_document.content is None:\n                    continue\n\n                for rank, retrieved_document in enumerate(retrieved):\n                    if retrieved_document.content is None:\n                        continue\n\n                    if ground_document.content in retrieved_document.content:\n                        score = 1 / (rank + 1)\n                        break\n            individual_scores.append(score)\n\n        score = sum(individual_scores) / len(retrieved_documents)\n\n        return {\"score\": score, \"individual_scores\": individual_scores}\n", "path": "haystack/components/evaluators/document_mrr.py"}, {"content": "# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>\n#\n# SPDX-License-Identifier: Apache-2.0\n\nfrom typing import Any, Dict, List\n\nfrom haystack import Document, component\n\n\n@component\nclass DocumentMAPEvaluator:\n    \"\"\"\n    A Mean Average Precision (MAP) evaluator for documents.\n\n    Evaluator that calculates the mean average precision of the retrieved documents, a metric\n    that measures how high retrieved documents are ranked.\n    Each question can have multiple ground truth documents and multiple retrieved documents.\n\n    `DocumentMAPEvaluator` doesn't normalize its inputs, the `DocumentCleaner` component\n    should be used to clean and normalize the documents before passing them to this evaluator.\n\n    Usage example:\n    ```python\n    from haystack import Document\n    from haystack.components.evaluators import DocumentMAPEvaluator\n\n    evaluator = DocumentMAPEvaluator()\n    result = evaluator.run(\n        ground_truth_documents=[\n            [Document(content=\"France\")],\n            [Document(content=\"9th century\"), Document(content=\"9th\")],\n        ],\n        retrieved_documents=[\n            [Document(content=\"France\")],\n            [Document(content=\"9th century\"), Document(content=\"10th century\"), Document(content=\"9th\")],\n        ],\n    )\n\n    print(result[\"individual_scores\"])\n    # [1.0, 0.8333333333333333]\n    print(result[\"score\"])\n    # 0.9166666666666666\n    ```\n    \"\"\"\n\n    @component.output_types(score=float, individual_scores=List[float])\n    def run(\n        self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]\n    ) -> Dict[str, Any]:\n        \"\"\"\n        Run the DocumentMAPEvaluator on the given inputs.\n\n        All lists must have the same length.\n\n        :param ground_truth_documents:\n            A list of expected documents for each question.\n        :param retrieved_documents:\n            A list of retrieved documents for each question.\n        :returns:\n            A dictionary with the following outputs:\n            - `score` - The average of calculated scores.\n            - `individual_scores` - A list of numbers from 0.0 to 1.0 that represents how high retrieved documents are ranked.\n        \"\"\"\n        if len(ground_truth_documents) != len(retrieved_documents):\n            msg = \"The length of ground_truth_documents and retrieved_documents must be the same.\"\n            raise ValueError(msg)\n\n        individual_scores = []\n\n        for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):\n            score = 0.0\n            for ground_document in ground_truth:\n                if ground_document.content is None:\n                    continue\n\n                average_precision = 0.0\n                relevant_documents = 0\n\n                for rank, retrieved_document in enumerate(retrieved):\n                    if retrieved_document.content is None:\n                        continue\n\n                    if ground_document.content in retrieved_document.content:\n                        relevant_documents += 1\n                        average_precision += relevant_documents / (rank + 1)\n                if relevant_documents > 0:\n                    score = average_precision / relevant_documents\n            individual_scores.append(score)\n\n        score = sum(individual_scores) / len(retrieved_documents)\n\n        return {\"score\": score, \"individual_scores\": individual_scores}\n", "path": "haystack/components/evaluators/document_map.py"}], "after_files": [{"content": "# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>\n#\n# SPDX-License-Identifier: Apache-2.0\n\nfrom typing import Any, Dict, List\n\nfrom haystack import Document, component\n\n\n@component\nclass DocumentMRREvaluator:\n    \"\"\"\n    Evaluator that calculates the mean reciprocal rank of the retrieved documents.\n\n    MRR measures how high the first retrieved document is ranked.\n    Each question can have multiple ground truth documents and multiple retrieved documents.\n\n    `DocumentMRREvaluator` doesn't normalize its inputs, the `DocumentCleaner` component\n    should be used to clean and normalize the documents before passing them to this evaluator.\n\n    Usage example:\n    ```python\n    from haystack import Document\n    from haystack.components.evaluators import DocumentMRREvaluator\n\n    evaluator = DocumentMRREvaluator()\n    result = evaluator.run(\n        ground_truth_documents=[\n            [Document(content=\"France\")],\n            [Document(content=\"9th century\"), Document(content=\"9th\")],\n        ],\n        retrieved_documents=[\n            [Document(content=\"France\")],\n            [Document(content=\"9th century\"), Document(content=\"10th century\"), Document(content=\"9th\")],\n        ],\n    )\n    print(result[\"individual_scores\"])\n    # [1.0, 1.0]\n    print(result[\"score\"])\n    # 1.0\n    ```\n    \"\"\"\n\n    # Refer to https://www.pinecone.io/learn/offline-evaluation/ for the algorithm.\n    @component.output_types(score=float, individual_scores=List[float])\n    def run(\n        self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]\n    ) -> Dict[str, Any]:\n        \"\"\"\n        Run the DocumentMRREvaluator on the given inputs.\n\n        `ground_truth_documents` and `retrieved_documents` must have the same length.\n\n        :param ground_truth_documents:\n            A list of expected documents for each question.\n        :param retrieved_documents:\n            A list of retrieved documents for each question.\n        :returns:\n            A dictionary with the following outputs:\n            - `score` - The average of calculated scores.\n            - `individual_scores` - A list of numbers from 0.0 to 1.0 that represents how high the first retrieved document is ranked.\n        \"\"\"\n        if len(ground_truth_documents) != len(retrieved_documents):\n            msg = \"The length of ground_truth_documents and retrieved_documents must be the same.\"\n            raise ValueError(msg)\n\n        individual_scores = []\n\n        for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):\n            reciprocal_rank = 0.0\n\n            ground_truth_contents = [doc.content for doc in ground_truth if doc.content is not None]\n            for rank, retrieved_document in enumerate(retrieved):\n                if retrieved_document.content is None:\n                    continue\n                if retrieved_document.content in ground_truth_contents:\n                    reciprocal_rank = 1 / (rank + 1)\n                    break\n            individual_scores.append(reciprocal_rank)\n\n        score = sum(individual_scores) / len(ground_truth_documents)\n\n        return {\"score\": score, \"individual_scores\": individual_scores}\n", "path": "haystack/components/evaluators/document_mrr.py"}, {"content": "# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>\n#\n# SPDX-License-Identifier: Apache-2.0\n\nfrom typing import Any, Dict, List\n\nfrom haystack import Document, component\n\n\n@component\nclass DocumentMAPEvaluator:\n    \"\"\"\n    A Mean Average Precision (MAP) evaluator for documents.\n\n    Evaluator that calculates the mean average precision of the retrieved documents, a metric\n    that measures how high retrieved documents are ranked.\n    Each question can have multiple ground truth documents and multiple retrieved documents.\n\n    `DocumentMAPEvaluator` doesn't normalize its inputs, the `DocumentCleaner` component\n    should be used to clean and normalize the documents before passing them to this evaluator.\n\n    Usage example:\n    ```python\n    from haystack import Document\n    from haystack.components.evaluators import DocumentMAPEvaluator\n\n    evaluator = DocumentMAPEvaluator()\n    result = evaluator.run(\n        ground_truth_documents=[\n            [Document(content=\"France\")],\n            [Document(content=\"9th century\"), Document(content=\"9th\")],\n        ],\n        retrieved_documents=[\n            [Document(content=\"France\")],\n            [Document(content=\"9th century\"), Document(content=\"10th century\"), Document(content=\"9th\")],\n        ],\n    )\n\n    print(result[\"individual_scores\"])\n    # [1.0, 0.8333333333333333]\n    print(result[\"score\"])\n    # 0.9166666666666666\n    ```\n    \"\"\"\n\n    # Refer to https://www.pinecone.io/learn/offline-evaluation/ for the algorithm.\n    @component.output_types(score=float, individual_scores=List[float])\n    def run(\n        self, ground_truth_documents: List[List[Document]], retrieved_documents: List[List[Document]]\n    ) -> Dict[str, Any]:\n        \"\"\"\n        Run the DocumentMAPEvaluator on the given inputs.\n\n        All lists must have the same length.\n\n        :param ground_truth_documents:\n            A list of expected documents for each question.\n        :param retrieved_documents:\n            A list of retrieved documents for each question.\n        :returns:\n            A dictionary with the following outputs:\n            - `score` - The average of calculated scores.\n            - `individual_scores` - A list of numbers from 0.0 to 1.0 that represents how high retrieved documents are ranked.\n        \"\"\"\n        if len(ground_truth_documents) != len(retrieved_documents):\n            msg = \"The length of ground_truth_documents and retrieved_documents must be the same.\"\n            raise ValueError(msg)\n\n        individual_scores = []\n\n        for ground_truth, retrieved in zip(ground_truth_documents, retrieved_documents):\n            average_precision = 0.0\n            average_precision_numerator = 0.0\n            relevant_documents = 0\n\n            ground_truth_contents = [doc.content for doc in ground_truth if doc.content is not None]\n            for rank, retrieved_document in enumerate(retrieved):\n                if retrieved_document.content is None:\n                    continue\n\n                if retrieved_document.content in ground_truth_contents:\n                    relevant_documents += 1\n                    average_precision_numerator += relevant_documents / (rank + 1)\n            if relevant_documents > 0:\n                average_precision = average_precision_numerator / relevant_documents\n            individual_scores.append(average_precision)\n\n        score = sum(individual_scores) / len(ground_truth_documents)\n        return {\"score\": score, \"individual_scores\": individual_scores}\n", "path": "haystack/components/evaluators/document_map.py"}]}