{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "conv-student-000",
   "metadata": {},
   "source": [
    "# Homework 1\n",
    "\n",
    "Σε αυτή την εργασία θα συνδέσουμε έναν μικρό υπολογισμό στο χαρτί με την έξοδο ενός προγράμματος και θα δούμε πώς διαλέγουμε έναν χρήσιμο έλεγχο.\n",
    "\n",
    "Τα βήματα είναι:\n",
    "\n",
    "- **A:** βρίσκουμε το μέγεθος της εξόδου και μία τιμή της\n",
    "- **B:** συμπληρώνουμε μια μικρή υλοποίηση και τη συγκρίνουμε με τη SciPy\n",
    "- **Γ:** εντοπισμός λάθους\n",
    "- **Δ:** επιλέγουμε ένα δικό μας ερώτημα και κάνουμε μία μικρή διερεύνηση\n",
    "- Προαιρετικό feedback στο τέλος\n",
    "\n",
    "**Σχετικό υλικό:** φυλλάδιο 3, ασκήσεις 1.1, 1.2 και 5.1. Μπορείτε να συμβουλευτείτε τις λυμένες ασκήσεις των φροντιστηρίων."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-001",
   "metadata": {},
   "source": [
    "## Πριν ξεκινήσουμε\n",
    "\n",
    "Η άσκηση χρησιμοποιεί τις βιβλιοθήκες NumPy, SciPy και Matplotlib.\n",
    "Για να τις εγκαταστήσουμε, εκτελούμε σε ξεχωριστό κελί `%pip install numpy scipy matplotlib` και κάνουμε επανεκκίνηση τον kernel.\n",
    "\n",
    "Το αρχικό notebook εκτελείται και με τα κενά ασυμπλήρωτα. Το `None` σημαίνει «δεν έχει δοθεί ακόμη απάντηση». Το `np.nan` χρησιμοποιείται εδώ ως θέση για έναν αριθμό που λείπει. Οι βοηθητικές συναρτήσεις δίνονται. Συμπληρώστε τον κώδικά σας ανάμεσα στα `### TODO: ADD YOUR CODE HERE` και `### END of TODO`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "student-identification",
   "metadata": {
    "ecey523": {
     "answer_id": "identity"
    }
   },
   "outputs": [],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "student_name = \"Name Surname\"  # Αντικαταστήστε με το όνομά σας.\n",
    "student_id = \"12345678\"  # Αντικαταστήστε με τον αριθμό μητρώου σας.\n",
    "### END of TODO"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "homework-imports",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy.signal import convolve2d"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-002",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.382056Z",
     "iopub.status.busy": "2026-10-31T15:34:38.381985Z",
     "iopub.status.idle": "2026-10-31T15:34:38.740672Z",
     "shell.execute_reply": "2026-10-31T15:34:38.740381Z"
    }
   },
   "source": [
    "## Βήμα Α\n",
    "\n",
    "Χρησιμοποιούμε την είσοδο και τον πυρήνα της άσκησης 1.1. Η αρίθμηση ξεκινά από το 0. Στο `X[m, n]`, ο πρώτος δείκτης επιλέγει γραμμή και ο δεύτερος στήλη."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "conv-student-004",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T20:01:11.496928Z",
     "iopub.status.busy": "2026-10-31T20:01:11.496795Z",
     "iopub.status.idle": "2026-10-31T20:01:11.500702Z",
     "shell.execute_reply": "2026-10-31T20:01:11.498935Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Είσοδος X:\n",
      " [[ 1.  2.  0.]\n",
      " [ 3.  1. -1.]\n",
      " [ 2.  0.  1.]]\n",
      "Πυρήνας H:\n",
      " [[ 1.  2.]\n",
      " [-1.  0.]]\n"
     ]
    }
   ],
   "source": [
    "X = np.array([[1, 2, 0],\n",
    "              [3, 1, -1],\n",
    "              [2, 0, 1]], dtype=float)\n",
    "H = np.array([[1, 2],\n",
    "              [-1, 0]], dtype=float)\n",
    "print(\"Είσοδος X:\\n\", X)\n",
    "print(\"Πυρήνας H:\\n\", H)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-004-2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.742001Z",
     "iopub.status.busy": "2026-10-31T15:34:38.741880Z",
     "iopub.status.idle": "2026-10-31T15:34:38.744691Z",
     "shell.execute_reply": "2026-10-31T15:34:38.744412Z"
    }
   },
   "source": [
    "Το $X$ είναι ο πίνακας εισόδου και το $H$ ο πυρήνας, δηλαδή τα βάρη του φίλτρου. Το $y[m,n]$ είναι μία τιμή εξόδου. Για τον συγκεκριμένο πυρήνα:\n",
    "\n",
    "$$y[m,n]=\\sum_{i=0}^{1}\\sum_{j=0}^{1}H[i,j]X[m-i,n-j].$$\n",
    "\n",
    "Οι δείκτες $m,n$ διαλέγουν τη θέση εξόδου. Οι $i,j$ διατρέχουν τις γραμμές και τις στήλες του πυρήνα. Τα δύο αθροίσματα σημαίνουν ότι προσθέτουμε τέσσερα γινόμενα. Κάθε τιμή του $X$ έξω από τον δοσμένο πίνακα θεωρείται μηδέν.\n",
    "\n",
    "### Α1. Μέγεθος της εξόδου\n",
    "Αν η είσοδος έχει $M$ γραμμές και $N$ στήλες και ο πυρήνας $M_h$ γραμμές και $N_h$ στήλες, η πλήρης έξοδος έχει σχήμα\n",
    "\n",
    "$$(M+M_h-1,\\ N+N_h-1).$$\n",
    "\n",
    "Το κάτω $h$ ξεχωρίζει τις διαστάσεις του πυρήνα από αυτές της εισόδου. Το σχήμα γράφεται ως `(γραμμές, στήλες)`. Αυτή η γραφή ονομάζεται **tuple** και κρατά τις δύο τιμές μαζί. Για παράδειγμα, ένας πίνακας με 2 γραμμές και 5 στήλες έχει σχήμα `(2, 5)`.\n",
    "\n",
    "Βρείτε το σχήμα της εξόδου για τους παραπάνω πίνακες και συμπληρώστε το `shape_by_hand` με τον ίδιο τρόπο.\n",
    "\n",
    "### Α2. Μία τιμή με το χέρι\n",
    "Στο μάθημα υπολογίστηκε το $y[1,1]=5$. Τώρα βρείτε το **$y[1,2]$**, δηλαδή τη δεύτερη γραμμή και την τρίτη στήλη εξόδου. Συμπληρώστε πρώτα τον πίνακα στο ξεχωριστό κελί απάντησης που ακολουθεί, χωρίς να καλέσετε τη SciPy.\n",
    "\n",
    "Προσθέστε τα τέσσερα γινόμενα και γράψτε το αποτέλεσμα στο `value_by_hand`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "answer-alpha2-table",
   "metadata": {
    "ecey523": {
     "answer_id": "a_table"
    }
   },
   "source": [
    "**Απάντηση Α2**\n",
    "\n",
    "| $(i,j)$ | $H[i,j]$ | Θέση $X[1-i,2-j]$ | Τιμή εισόδου | Γινόμενο |\n",
    "|---|---:|---|---:|---:|\n",
    "| $(0,0)$ | 1 | … | … | … |\n",
    "| $(0,1)$ | 2 | … | … | … |\n",
    "| $(1,0)$ | −1 | … | … | … |\n",
    "| $(1,1)$ | 0 | … | … | … |\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "conv-student-006",
   "metadata": {
    "ecey523": {
     "answer_id": "a_values"
    },
    "execution": {
     "iopub.execute_input": "2026-10-31T20:01:11.502999Z",
     "iopub.status.busy": "2026-10-31T20:01:11.502912Z",
     "iopub.status.idle": "2026-10-31T20:01:11.512460Z",
     "shell.execute_reply": "2026-10-31T20:01:11.511885Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Πρόβλεψη σχήματος: None\n",
      "Υπολογισμός y[1, 2]: None\n"
     ]
    }
   ],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "shape_by_hand = None  # Αντικαταστήστε με το ζεύγος (γραμμές, στήλες) της εξόδου, σε μορφή tuple, π.χ. (1, 2).\n",
    "value_by_hand = None  # Αντικαταστήστε με την τιμή y[1, 2].\n",
    "### END of TODO\n",
    "\n",
    "print(\"Πρόβλεψη σχήματος:\", shape_by_hand)\n",
    "print(\"Υπολογισμός y[1, 2]:\", value_by_hand)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-006-2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.745894Z",
     "iopub.status.busy": "2026-10-31T15:34:38.745791Z",
     "iopub.status.idle": "2026-10-31T15:34:38.755656Z",
     "shell.execute_reply": "2026-10-31T15:34:38.755459Z"
    }
   },
   "source": [
    "## Βήμα Β\n",
    "### Β1. Δισδιάστατη Συνέλιξη\n",
    "\n",
    "Η `conv2d_full(x, h)` υπολογίζει την πλήρη συνέλιξη δύο μη κενών, πραγματικών, δισδιάστατων arrays. Τα πεζά `x, h` είναι οι παράμετροι της συνάρτησης. Όταν την καλέσουμε με `X, H`, θα χρησιμοποιήσει τους πίνακες του Α.\n",
    "\n",
    "Συμπληρώστε **τις δύο περιοχές TODO**:\n",
    "\n",
    "1. Τους δείκτες `mi, nj` της εισόδου που αντιστοιχούν στα `m, n, i, j`.\n",
    "2. Το γινόμενο που προστίθεται στο `y[m, n]`.\n",
    "\n",
    "Οι βρόχοι, το μέγεθος της εξόδου, ο έλεγχος ορίων και το `return` είναι έτοιμα.\n",
    "\n",
    "Στη NumPy οι αρνητικοί δείκτες διαβάζουν στοιχεία από το τέλος του array. Ο έλεγχος ορίων εξασφαλίζει ότι στον υπολογισμό συμμετέχουν μόνο τα δείγματα μέσα στην είσοδο, σύμφωνα με τη μηδενική επέκταση.\n",
    "\n",
    "Ακολουθήστε τον τύπο του βήματος Α. Σε αυτόν τον τύπο η αναστροφή εκφράζεται μέσω των δεικτών της εισόδου."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "conv-student-010",
   "metadata": {
    "ecey523": {
     "answer_id": "b_function"
    },
    "execution": {
     "iopub.execute_input": "2026-10-31T20:01:11.513923Z",
     "iopub.status.busy": "2026-10-31T20:01:11.513852Z",
     "iopub.status.idle": "2026-10-31T20:01:11.516231Z",
     "shell.execute_reply": "2026-10-31T20:01:11.515825Z"
    }
   },
   "outputs": [],
   "source": [
    "def conv2d_full(x, h):\n",
    "    x = np.asarray(x, dtype=float)\n",
    "    h = np.asarray(h, dtype=float)\n",
    "    M, N = x.shape\n",
    "    Mh, Nh = h.shape\n",
    "    y = np.zeros((M + Mh - 1, N + Nh - 1), dtype=float)\n",
    "\n",
    "    for m in range(y.shape[0]):\n",
    "        for n in range(y.shape[1]):\n",
    "            for i in range(Mh):\n",
    "                for j in range(Nh):\n",
    "                    ### TODO: ADD YOUR CODE HERE\n",
    "                    mi, nj = None, None\n",
    "                    ### END of TODO\n",
    "                    if mi is None or nj is None:\n",
    "                        return np.full(y.shape, np.nan)\n",
    "                    if 0 <= mi < M and 0 <= nj < N:\n",
    "                        ### TODO: ADD YOUR CODE HERE\n",
    "                        y[m, n] += np.nan\n",
    "                        ### END of TODO\n",
    "    return y"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-010-2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.756596Z",
     "iopub.status.busy": "2026-10-31T15:34:38.756526Z",
     "iopub.status.idle": "2026-10-31T15:34:38.759009Z",
     "shell.execute_reply": "2026-10-31T15:34:38.758772Z"
    }
   },
   "source": [
    "### Β2. Συνέλιξη με χρήση SciPy\n",
    "\n",
    "Η SciPy παρέχει μια έτοιμη υλοποίηση. Με `mode=\"full\"` κρατάμε ολόκληρη την έξοδο. Με `boundary=\"fill\", fillvalue=0` ορίζουμε ότι οι τιμές έξω από την είσοδο είναι μηδενικές. Η SciPy χειρίζεται ήδη τη συνέλιξη, οπότε της δίνουμε τον αρχικό πυρήνα.\n",
    "\n",
    "Η `compare_outputs` εμφανίζει τα σχήματα των δύο αποτελεσμάτων. Αν είναι ίδια, υπολογίζει τη μέγιστη απόλυτη διαφορά, δηλαδή τη μεγαλύτερη απόσταση ανάμεσα σε δύο αντίστοιχες τιμές. Η `np.allclose` επιστρέφει `True` όταν όλες οι αντίστοιχες τιμές συμφωνούν μέσα σε μια μικρή αριθμητική ανοχή.\n",
    "\n",
    "Τιμή πολύ κοντά στο μηδέν δείχνει αριθμητική συμφωνία στο συγκεκριμένο παράδειγμα. Προσέξτε ότι μικρές διαφορές, όπως `1e-15`, μπορεί να οφείλονται στη σειρά των πράξεων κινητής υποδιαστολής.\n",
    "\n",
    "Όσο υπάρχουν `np.nan` στα TODO της συνάρτησης, η διαφορά εμφανίζεται ως `nan` και η αριθμητική συμφωνία ως `False`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "conv-student-012",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T20:01:11.517774Z",
     "iopub.status.busy": "2026-10-31T20:01:11.517694Z",
     "iopub.status.idle": "2026-10-31T20:01:11.521942Z",
     "shell.execute_reply": "2026-10-31T20:01:11.521515Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Το παράδειγμα του Α\n",
      "Σχήμα δικό μας: (4, 4)\n",
      "Σχήμα αναφοράς: (4, 4)\n",
      "Μέγιστη απόλυτη διαφορά: nan\n",
      "Αριθμητική συμφωνία: False\n",
      "Η έξοδος αναφοράς:\n",
      " [[ 1.  4.  4.  0.]\n",
      " [ 2.  5.  1. -2.]\n",
      " [-1.  3.  2.  2.]\n",
      " [-2.  0. -1.  0.]]\n",
      "Η έξοδος που υπολογίσαμε:\n",
      " [[nan nan nan nan]\n",
      " [nan nan nan nan]\n",
      " [nan nan nan nan]\n",
      " [nan nan nan nan]]\n",
      "Σχήμα που προβλέψαμε: None\n",
      "Σχήμα αναφοράς: (4, 4)\n",
      "y[1, 2] με το χέρι: None\n",
      "y[1, 2] αναφοράς: 1.0\n"
     ]
    }
   ],
   "source": [
    "def compare_outputs(actual, reference, label):\n",
    "    print(label)\n",
    "    print(\"Σχήμα δικό μας:\", actual.shape)\n",
    "    print(\"Σχήμα αναφοράς:\", reference.shape)\n",
    "    if actual.shape != reference.shape:\n",
    "        print(\"Διαφορετικά σχήματα. Ελέγξτε τις διαστάσεις.\")\n",
    "        return\n",
    "    print(\"Μέγιστη απόλυτη διαφορά:\", np.max(np.abs(actual - reference)))\n",
    "    print(\"Αριθμητική συμφωνία:\", np.allclose(actual, reference))\n",
    "\n",
    "Y_reference = convolve2d(X, H, mode=\"full\", boundary=\"fill\", fillvalue=0)\n",
    "Y_mine = conv2d_full(X, H)\n",
    "compare_outputs(Y_mine, Y_reference, \"Το παράδειγμα του Α\")\n",
    "\n",
    "print(\"Η έξοδος αναφοράς:\\n\", Y_reference)\n",
    "print(\"Η έξοδος που υπολογίσαμε:\\n\", Y_mine)\n",
    "print(\"Σχήμα που προβλέψαμε:\", shape_by_hand)\n",
    "print(\"Σχήμα αναφοράς:\", Y_reference.shape)\n",
    "print(\"y[1, 2] με το χέρι:\", value_by_hand)\n",
    "print(\"y[1, 2] αναφοράς:\", Y_reference[1, 2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-012-2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.759811Z",
     "iopub.status.busy": "2026-10-31T15:34:38.759740Z",
     "iopub.status.idle": "2026-10-31T15:34:38.762424Z",
     "shell.execute_reply": "2026-10-31T15:34:38.762229Z"
    }
   },
   "source": [
    "### Β3. Έλεγχος ορθότητας\n",
    "\n",
    "Για να επαληθεύσουμε την ορθότητα του αλγορίθμου μας, μπορούμε να σχεδιάσουμε μικρά \"τεστ\", σκοπός των οποίων είναι να ελέγξουν την ορθότητα της λύσης μας σε διαφορετικά είδη εισόδου. Έτσι εξετάζουμε την υλοποίηση σε περισσότερες περιπτώσεις. Στον προγραμματισμό, αυτά τα τεστ ονομάζονται \"unit tests\" και η χρήση τους ενδείκνυται για τον έλεγχο οριακών τιμών (corner cases), τη διαχείριση σφαλμάτων (error handling) κλπ.\n",
    "\n",
    "Ελέγχοντας διαφορετικές εισόδους, μπορούμε να εντοπίσουμε λάθη που δεν εμφανίζονται στο αρχικό παράδειγμα και να αποκτήσουμε μεγαλύτερη εμπιστοσύνη στην υλοποίησή μας. Εδώ, θα ελέγξουμε την εξής συνθήκη: Στο πρώτο παράδειγμα η είσοδος 3×3 και ο πυρήνας 2×2 είναι τετράγωνοι. Αν χρησιμοποιήσουμε κατά λάθος το πλήθος γραμμών στη θέση του πλήθους στηλών, το σφάλμα μπορεί να περάσει απαρατήρητο, επειδή οι δύο αριθμοί είναι ίσοι. Ας δούμε τι συμβαίνει αν θέσουμε μια είσοδο 4×6 και πυρήνα 3×2, όπου οι διαστάσεις διαφέρουν. Η πλήρης έξοδος έχει σχήμα `(6, 7)`, οπότε ελέγχουμε διαφορετικό πλήθος γραμμών και στηλών και στο αποτέλεσμα.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "conv-student-014",
   "metadata": {},
   "outputs": [],
   "source": [
    "rng = np.random.default_rng(4)\n",
    "x_rect = rng.normal(size=(4, 6))\n",
    "h_rect = rng.normal(size=(3, 2))\n",
    "y_rect_ref = convolve2d(x_rect, h_rect, mode=\"full\", boundary=\"fill\", fillvalue=0)\n",
    "y_rect_mine = conv2d_full(x_rect, h_rect)\n",
    "compare_outputs(y_rect_mine, y_rect_ref, \"Ορθογώνια δεδομένα\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "beta3-own-check-instructions",
   "metadata": {},
   "source": [
    "### Β4. Ένας δικός σας έλεγχος\n",
    "\n",
    "Επιλέξτε μία ακόμη μικρή είσοδο και έναν πυρήνα για να ελέγξετε μια διαφορετική περίπτωση της `conv2d_full`. Εξηγήστε σύντομα γιατί επιλέξατε αυτά τα δεδομένα και τι περιμένετε να συμβεί. Συμπληρώστε τα δύο arrays στο κελί κώδικα και συγκρίνετε την έξοδο με τη SciPy. Έπειτα προσθέστε στην απάντησή σας αν το αποτέλεσμα συμφωνεί με την πρόβλεψή σας.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "beta3-own-check-answer",
   "metadata": {
    "ecey523": {
     "answer_id": "b4_reason"
    }
   },
   "source": [
    "*Γράψτε εδώ την επιλογή σας, την πρόβλεψη και, μετά την εκτέλεση, το συμπέρασμά σας.*\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "beta3-own-check-code",
   "metadata": {
    "ecey523": {
     "answer_id": "b4_code"
    }
   },
   "outputs": [],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "x_check = None  # Συμπληρώστε ένα μικρό δισδιάστατο np.array.\n",
    "h_check = None  # Συμπληρώστε τον πυρήνα ως δισδιάστατο np.array.\n",
    "### END of TODO\n",
    "\n",
    "if x_check is not None and h_check is not None:\n",
    "    y_check = conv2d_full(x_check, h_check)\n",
    "    y_check_ref = convolve2d(x_check, h_check, mode=\"full\", boundary=\"fill\", fillvalue=0)\n",
    "    compare_outputs(y_check, y_check_ref, \"Ο δικός μας έλεγχος\")\n",
    "    print(\"Η έξοδος που υπολογίσαμε:\\n\", y_check)\n",
    "    print(\"Η έξοδος αναφοράς:\\n\", y_check_ref)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-014-2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.763139Z",
     "iopub.status.busy": "2026-10-31T15:34:38.763035Z",
     "iopub.status.idle": "2026-10-31T15:34:38.765025Z",
     "shell.execute_reply": "2026-10-31T15:34:38.764790Z"
    }
   },
   "source": [
    "## Βήμα Γ \n",
    "### Γ1. Eντοπισμός λάθους\n",
    "\n",
    "Η `conv2d_candidate` έχει ένα σφάλμα στον υπολογισμό. Εκτελείται και επιστρέφει το σωστό σχήμα. **Πρώτα παρατηρήστε τα αποτελέσματά της.**\n",
    "\n",
    "Θα συγκρίνουμε την ίδια συνάρτηση με τη SciPy, πρώτα για έναν πυρήνα με όλα τα βάρη ίσα με 1 και μετά για τον μη συμμετρικό `H` του Α. Προβλέψτε σε ποια περίπτωση είναι πιο εύκολο να φανεί ένα λάθος στην αναστροφή.\n",
    "\n",
    "Μην κάνετε ακόμη καμία αλλαγή στη συνάρτηση."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "conv-student-018",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T20:01:11.526394Z",
     "iopub.status.busy": "2026-10-31T20:01:11.526320Z",
     "iopub.status.idle": "2026-10-31T20:01:11.528514Z",
     "shell.execute_reply": "2026-10-31T20:01:11.528227Z"
    }
   },
   "outputs": [],
   "source": [
    "def conv2d_candidate(x, h):\n",
    "    x = np.asarray(x, dtype=float)\n",
    "    h = np.asarray(h, dtype=float)\n",
    "    M, N = x.shape\n",
    "    Mh, Nh = h.shape\n",
    "    y = np.zeros((M + Mh - 1, N + Nh - 1), dtype=float)\n",
    "    for m in range(y.shape[0]):\n",
    "        for n in range(y.shape[1]):\n",
    "            for i in range(Mh):\n",
    "                for j in range(Nh):\n",
    "                    mi, nj = m - i, n - j\n",
    "                    if 0 <= mi < M and 0 <= nj < N:\n",
    "                        y[m, n] += h[Mh - 1 - i, Nh - 1 - j] * x[mi, nj]\n",
    "    return y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "conv-student-018-2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.765606Z",
     "iopub.status.busy": "2026-10-31T15:34:38.765488Z",
     "iopub.status.idle": "2026-10-31T15:34:38.767579Z",
     "shell.execute_reply": "2026-10-31T15:34:38.767316Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Συμμετρικός πυρήνας\n",
      "Σχήμα δικό μας: (4, 4)\n",
      "Σχήμα αναφοράς: (4, 4)\n",
      "Μέγιστη απόλυτη διαφορά: 0.0\n",
      "Αριθμητική συμφωνία: True\n",
      "Μη συμμετρικός πυρήνας\n",
      "Σχήμα δικό μας: (4, 4)\n",
      "Σχήμα αναφοράς: (4, 4)\n",
      "Μέγιστη απόλυτη διαφορά: 7.0\n",
      "Αριθμητική συμφωνία: False\n"
     ]
    }
   ],
   "source": [
    "H_symmetric = np.ones((2, 2))\n",
    "for label, kernel in [(\"Συμμετρικός πυρήνας\", H_symmetric),\n",
    "                      (\"Μη συμμετρικός πυρήνας\", H)]:\n",
    "    expected = convolve2d(X, kernel, mode=\"full\", boundary=\"fill\", fillvalue=0)\n",
    "    candidate = conv2d_candidate(X, kernel)\n",
    "    compare_outputs(candidate, expected, label)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-019",
   "metadata": {},
   "source": [
    "#### Γ1.1. Ο πρώτος πυρήνας\n",
    "\n",
    "Ο πυρήνας με όλους τους συντελεστές ίσους με 1 αλλάζει όταν περιστραφεί κατά 180°; Συμπληρώστε την απάντηση στο επόμενο κελί. Έπειτα εξηγήστε σύντομα πώς αυτό επηρεάζει τη δυνατότητά μας να εντοπίσουμε μια λανθασμένη αναστροφή.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "gamma1-structured",
   "metadata": {
    "ecey523": {
     "answer_id": "g1_flip_values"
    }
   },
   "outputs": [],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "gamma1_kernel_changes = None  # True αν αλλάζει, False αν παραμένει ίδιος.\n",
    "### END of TODO\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-021",
   "metadata": {
    "ecey523": {
     "answer_id": "g1_reason"
    }
   },
   "source": [
    "**Αιτιολόγηση Γ1.1**\n",
    "\n",
    "*Γράψτε εδώ την αιτιολόγησή σας.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "gamma1-factor-question",
   "metadata": {},
   "source": [
    "#### Γ1.2. Εντοπισμός του λανθασμένου όρου\n",
    "\n",
    "Στη γραμμή που ενημερώνει το `y[m, n]`, ποιος όρος διαβάζει τον πυρήνα με ανεστραμμένους δείκτες; Γράψτε μέσα στα εισαγωγικά τον όρο `h[...]` που υπάρχει στη λανθασμένη συνάρτηση. Τη διόρθωση της συνάρτησης θα την κάνουμε στο Γ3.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "gamma1-factor-answer",
   "metadata": {
    "ecey523": {
     "answer_id": "g1_factor_values"
    }
   },
   "outputs": [],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "gamma1_faulty_term = \"h[...]\"  # Ο όρος h[...] που εντοπίσατε.\n",
    "### END of TODO\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "gamma1-shape-question",
   "metadata": {},
   "source": [
    "#### Γ1.3. Έλεγχος του σχήματος\n",
    "\n",
    "Αρκεί το σωστό σχήμα εξόδου για να επιβεβαιώσουμε ότι η συνάρτηση υπολογίζει σωστά τη συνέλιξη; Συμπληρώστε την απάντησή σας και αιτιολογήστε τη με βάση τα αποτελέσματα που είδατε.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "gamma1-shape-answer",
   "metadata": {
    "ecey523": {
     "answer_id": "g1_shape_values"
    }
   },
   "outputs": [],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "gamma1_shape_sufficient = None  # True αν αρκεί ο έλεγχος του σχήματος, False αν χρειάζεται και άλλος έλεγχος.\n",
    "### END of TODO\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "gamma1-shape-reason",
   "metadata": {
    "ecey523": {
     "answer_id": "g1_shape_reason"
    }
   },
   "source": [
    "**Αιτιολόγηση Γ1.3**\n",
    "\n",
    "*Γράψτε εδώ την αιτιολόγησή σας.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-022",
   "metadata": {},
   "source": [
    "### Γ2. Έλεγχος με μοναδιαίο παλμό\n",
    "\n",
    "Ένας χρήσιμος έλεγχος έχει αποτέλεσμα που μπορούμε να προβλέψουμε εύκολα. Χρησιμοποιούμε ως είσοδο τον πίνακα `[[1]]`, με μηδενικές τιμές έξω από αυτόν. Αυτή η είσοδος είναι ο μοναδιαίος παλμός στην αρχή των δύο αξόνων.\n",
    "\n",
    "Η συνέλιξη αυτού του παλμού με τον πυρήνα `H` επιστρέφει τον ίδιο τον `H`. Το μοναδικό μη μηδενικό δείγμα της εισόδου έχει τιμή 1, οπότε αναπαράγει τα στοιχεία του πυρήνα στις αντίστοιχες θέσεις εξόδου.\n",
    "\n",
    "Εκτελέστε το επόμενο κελί και συγκρίνετε τους δύο πίνακες:\n",
    "\n",
    "1. Η `conv2d_candidate` επιστρέφει τον αναμενόμενο πίνακα `H`;\n",
    "2. Περιγράψτε πώς διαφέρει ο πίνακας που επέστρεψε από τον `H`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "conv-student-023",
   "metadata": {},
   "outputs": [],
   "source": [
    "impulse = np.array([[1.0]])\n",
    "impulse_output = conv2d_candidate(impulse, H)\n",
    "print(\"Αναμενόμενη έξοδος H:\\n\", H)\n",
    "print(\"Έξοδος της conv2d_candidate:\\n\", impulse_output)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "gamma2-structured",
   "metadata": {
    "ecey523": {
     "answer_id": "g2_values"
    }
   },
   "outputs": [],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "gamma2_matches_kernel = None  # True αν οι δύο πίνακες είναι ίσοι, False αν διαφέρουν.\n",
    "### END of TODO\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "answer-gamma-2",
   "metadata": {
    "ecey523": {
     "answer_id": "g2_reason"
    }
   },
   "source": [
    "**Περιγραφή της διαφοράς Γ2**\n",
    "\n",
    "*Περιγράψτε εδώ τη διαφορά που παρατηρείτε ανάμεσα στους δύο πίνακες.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-023-2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.772014Z",
     "iopub.status.busy": "2026-10-31T15:34:38.771889Z",
     "iopub.status.idle": "2026-10-31T15:34:38.774518Z",
     "shell.execute_reply": "2026-10-31T15:34:38.774137Z"
    }
   },
   "source": [
    "### Γ3. Διόρθωση λάθους\n",
    "\n",
    "Στο επόμενο κελί υπάρχει δεύτερη εκδοχή της συνάρτησης, για να διατηρήσουμε την αρχική λανθασμένη υλοποίηση ως παράδειγμα. Διορθώστε τη γραμμή ανάμεσα στα σχόλια TODO. Έπειτα εκτελέστε ξανά τον ορισμό και τις δύο συγκρίσεις που ακολουθούν."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "conv-student-025",
   "metadata": {
    "ecey523": {
     "answer_id": "g_function"
    },
    "execution": {
     "iopub.execute_input": "2026-10-31T20:01:11.535099Z",
     "iopub.status.busy": "2026-10-31T20:01:11.534947Z",
     "iopub.status.idle": "2026-10-31T20:01:11.537419Z",
     "shell.execute_reply": "2026-10-31T20:01:11.537258Z"
    }
   },
   "outputs": [],
   "source": [
    "def conv2d_repaired(x, h):\n",
    "    x = np.asarray(x, dtype=float)\n",
    "    h = np.asarray(h, dtype=float)\n",
    "    M, N = x.shape\n",
    "    Mh, Nh = h.shape\n",
    "    y = np.zeros((M + Mh - 1, N + Nh - 1), dtype=float)\n",
    "    for m in range(y.shape[0]):\n",
    "        for n in range(y.shape[1]):\n",
    "            for i in range(Mh):\n",
    "                for j in range(Nh):\n",
    "                    mi, nj = m - i, n - j\n",
    "                    if 0 <= mi < M and 0 <= nj < N:\n",
    "                        ### TODO: ADD YOUR CODE HERE\n",
    "                        y[m, n] += h[Mh - 1 - i, Nh - 1 - j] * x[mi, nj]\n",
    "                        ### END of TODO\n",
    "    return y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "conv-student-025-2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.775237Z",
     "iopub.status.busy": "2026-10-31T15:34:38.775112Z",
     "iopub.status.idle": "2026-10-31T15:34:38.777512Z",
     "shell.execute_reply": "2026-10-31T15:34:38.777256Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Μετά τη διόρθωση: παλμός\n",
      "Σχήμα δικό μας: (2, 2)\n",
      "Σχήμα αναφοράς: (2, 2)\n",
      "Μέγιστη απόλυτη διαφορά: 3.0\n",
      "Αριθμητική συμφωνία: False\n",
      "Μετά τη διόρθωση: αρχική είσοδος\n",
      "Σχήμα δικό μας: (4, 4)\n",
      "Σχήμα αναφοράς: (4, 4)\n",
      "Μέγιστη απόλυτη διαφορά: 7.0\n",
      "Αριθμητική συμφωνία: False\n"
     ]
    }
   ],
   "source": [
    "compare_outputs(conv2d_repaired(impulse, H), H, \"Μετά τη διόρθωση: παλμός\")\n",
    "compare_outputs(conv2d_repaired(X, H), Y_reference, \"Μετά τη διόρθωση: αρχική είσοδος\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-026",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-31T15:34:38.778394Z",
     "iopub.status.busy": "2026-10-31T15:34:38.778313Z",
     "iopub.status.idle": "2026-10-31T15:34:38.780637Z",
     "shell.execute_reply": "2026-10-31T15:34:38.780357Z"
    }
   },
   "source": [
    "## Βήμα Δ\n",
    "\n",
    "Διαλέξτε **ένα ερώτημα πάνω στην ύλη που έχει διδαχτεί μέχρι στιγμής στα φροντιστηριακά μαθήματα (Διαλέξεις 1–3)**. Σχεδιάστε ένα μικρό πείραμα που θα σας βοηθήσει να καταλάβετε καλύτερα το θέμα που επιλέξατε.\n",
    "\n",
    "### Επιλογή θέματος\n",
    "\n",
    "Επιλέξτε κάτι που σας ενδιαφέρει ή σας δυσκόλεψε και θα θέλατε να του αφιερώσετε επιπλέον χρόνο. Μπορείτε να ξεκινήσετε από κάποια άσκηση που έχουμε δει στο μάθημα, από τα φυλλάδια ασκήσεων ή από ένα δικό σας ερώτημα.\n",
    "\n",
    "Κάποιες ενδεικτικές κατευθύνσεις διερεύνησης είναι:\n",
    "\n",
    "- Οπτικοποίηση: Δημιουργήστε μια οπτική αναπαράσταση που βοηθά στην κατανόηση μιας μαθηματικής έννοιας ή μεθόδου.\n",
    "\n",
    "- Πειραματισμός: Τροποποιήστε μια γνωστή άσκηση ή μεταβάλετε τις παραμέτρους ενός προβλήματος και εξετάστε πώς επηρεάζεται το αποτέλεσμα.\n",
    "\n",
    "- Υλοποίηση: Διερευνήστε εναλλακτικούς τρόπους υλοποίησης μιας μεθόδου ή τη συμπεριφορά ενός αλγορίθμου.\n",
    "\n",
    "- Επαλήθευση: Ελέγξτε την ορθότητα μιας λύσης, μιας υπόθεσης ή ενός υπολογιστικού αποτελέσματος.\n",
    "\n",
    "Οι παραπάνω κατευθύνσεις είναι ενδεικτικές. Μπορείτε να ακολουθήσετε οποιαδήποτε άλλη προσέγγιση θεωρείτε ενδιαφέρουσα, αρκεί να σχετίζεται με την ύλη του μαθήματος.\n",
    "\n",
    "### Σχεδιασμός πειράματος\n",
    "\n",
    "Στόχος δεν είναι απλώς να αναπαράγετε μια γνωστή λύση ή να δημιουργήσετε ένα διάγραμμα, αλλά να εξετάσετε ένα συγκεκριμένο ερώτημα και να προσπαθήσετε να εξηγήσετε αυτό που παρατηρείτε.\n",
    "\n",
    "Σχεδιάστε μια **μικρή δοκιμή, σύγκριση ή οπτικοποίηση** που θα σας βοηθήσει να απαντήσετε στο ερώτημά σας. Σκεφτείτε ποια στοιχεία μεταβάλλετε, ποια παραμένουν σταθερά και τι περιμένετε να παρατηρήσετε.\n",
    "\n",
    "**Παράδειγμα**\n",
    "\n",
    "Ένα παράδειγμα διερεύνησης που δείξαμε στο φροντιστήριο ήταν το εξής: «Πώς επηρεάζουν οι ιδιοτιμές το σχήμα της έλλειψης;»\n",
    "\n",
    "Μπορούμε να κρατήσουμε σταθερή τη μία ιδιοτιμή, να μεταβάλουμε την άλλη και να σχεδιάσουμε τις αντίστοιχες ελλείψεις. Συγκρίνοντας τα αποτελέσματα, μπορούμε να εξετάσουμε πώς επηρεάζονται οι ημιάξονες και τι σημαίνει αυτό για την ευαισθησία του προβλήματος.\n",
    "\n",
    "Στην άσκηση 2.2 του φυλλαδίου 1 εξετάσαμε τι επηρεάζει το μέγεθος και τις αναλογίες της έλλειψης. Κρατώντας σταθερές τις ιδιοτιμές $12$ και $0.03$, σχεδιάζουμε τις καμπύλες για $c=1,4,9$, όπου $c$ είναι η αύξηση του κόστους από την ελάχιστη τιμή του. Ελέγχουμε αν ο λόγος $a_2/a_1$ παραμένει ίδιος καθώς μεγαλώνει η έλλειψη, όπου $a_1$ και $a_2$ είναι τα μήκη των δύο ημιαξόνων. Εναλλακτικά, κρατάμε σταθερά το $c=1$, την πρώτη ιδιοτιμή στο $12$ και τις διευθύνσεις των αξόνων. Μεταβάλλουμε τη δεύτερη ιδιοτιμή, σχεδιάζουμε ξανά την έλλειψη και εξετάζουμε πώς αλλάζει ο λόγος των ημιαξόνων. Σε κάθε πείραμα μεταβάλλουμε μία ποσότητα, ώστε να μπορούμε να εξηγήσουμε την επίδρασή της στο αποτέλεσμα.\n",
    "\n",
    "Αυτό είναι ένα παράδειγμα μικρής διερεύνησης: ξεκινάμε από μια απορία, αλλάζουμε κάτι συγκεκριμένο και παρατηρούμε το αποτέλεσμα.\n",
    "\n",
    "Δεν χρειάζεται το πείραμά σας να είναι πολύπλοκο ή να καταλήγει σε κάποιο εντυπωσιακό αποτέλεσμα. Ακόμη και αν το αποτέλεσμα διαψεύσει την αρχική σας πρόβλεψη, παραμένει μια χρήσιμη παρατήρηση.\n",
    "\n",
    "### Εργαλεία\n",
    "\n",
    "Η διερεύνησή σας πρέπει να περιλαμβάνει **κώδικα Python που εκτελείται στο notebook**. Μπορείτε να χρησιμοποιήσετε οποιαδήποτε βιβλιοθήκη θέλετε, καθώς και εργαλεία AI, όπως ChatGPT, Claude ή Gemini. Η χρήση AI επιτρέπεται και ενθαρρύνεται ως βοήθημα για την κατανόηση μιας έννοιας, τον σχεδιασμό ενός πειράματος, τη δημιουργία κώδικα ή την ερμηνεία αποτελεσμάτων.\n",
    "\n",
    "Σας προτρέπουμε, ωστόσο, να επιλέξετε πρώτα τι θέλετε να διερευνήσετε, με βάση τις προσωπικές σας απορίες και τα ενδιαφέροντά σας, και στη συνέχεια να χρησιμοποιήσετε τα εργαλεία για να το εξετάσετε."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "delta-deliverables",
   "metadata": {},
   "source": [
    "### Παραδοτέα\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "delta1-instructions",
   "metadata": {},
   "source": [
    "#### Δ1. Ερώτημα και αρχική πρόβλεψη\n",
    "\n",
    "Ποιο ερώτημα επιλέξατε, γιατί σας ενδιαφέρει και τι περιμένατε να συμβεί;\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "delta1-answer",
   "metadata": {
    "ecey523": {
     "answer_id": "d_question"
    }
   },
   "source": [
    "*Γράψτε εδώ.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "delta2-instructions",
   "metadata": {},
   "source": [
    "#### Δ2. Μέθοδος και εργαλεία\n",
    "\n",
    "Περιγράψτε τι μεταβάλατε, τι κρατήσατε σταθερό και πώς ελέγξατε το ερώτημά σας. Αν χρησιμοποιήσατε AI, αναφέρετε το μοντέλο και τα prompts.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "delta2-answer",
   "metadata": {
    "ecey523": {
     "answer_id": "d_method"
    }
   },
   "source": [
    "*Γράψτε εδώ.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "delta3-instructions",
   "metadata": {},
   "source": [
    "#### Δ3. Κώδικας πειράματος\n",
    "\n",
    "Συμπεριλάβετε τον κώδικα Python στο επόμενο κελί κώδικα.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "conv-student-031-2",
   "metadata": {
    "ecey523": {
     "answer_id": "d_code"
    },
    "execution": {
     "iopub.execute_input": "2026-10-31T20:01:11.753662Z",
     "iopub.status.busy": "2026-10-31T20:01:11.753573Z",
     "iopub.status.idle": "2026-10-31T20:01:11.757116Z",
     "shell.execute_reply": "2026-10-31T20:01:11.754986Z"
    }
   },
   "outputs": [],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "# Γράψτε εδώ τον κώδικα του πειράματός σας σε Python,\n",
    "# είτε πρόκειται για δικό σας κώδικα είτε για κώδικα από το εργαλείο AI που χρησιμοποιήσατε\n",
    "### END of TODO"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "delta4-instructions",
   "metadata": {},
   "source": [
    "#### Δ4. Αποτέλεσμα και παρατήρηση\n",
    "\n",
    "Παρουσιάστε το αποτέλεσμα με έξοδο κώδικα, πίνακα, διάγραμμα ή άλλη μορφή που σας εξυπηρετεί. Μπορείτε να αναφερθείτε στην έξοδο του Δ3. Να φαίνεται τι συγκρίνετε ή ελέγχετε. Εξηγήστε τι συμπεραίνετε, αν το αποτέλεσμα συμφωνεί με την πρόβλεψή σας και αν απαντά στο αρχικό ερώτημα. Προσθέστε τι θα θέλατε να ελέγξετε επιπλέον, εφόσον υπάρχει κάτι.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "delta4-answer",
   "metadata": {
    "ecey523": {
     "answer_id": "d_result"
    }
   },
   "source": [
    "*Γράψτε εδώ.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv-student-033",
   "metadata": {},
   "source": [
    "## Πριν παραδώσετε την εργασία\n",
    "\n",
    "- Κάντε **Restart Kernel και Run All** και αποθηκεύστε το notebook **με τα αποτελέσματα των κελιών ορατά**, συμπεριλαμβανομένων των πινάκων, των διαγραμμάτων και των εκτυπώσεων του βήματος Δ. Η εκτέλεση των κελιών κώδικα και η αποθήκευση των αποτελεσμάτων τους αποτελούν μέρος της αξιολόγησης. Μην κάνετε **Clear Outputs** πριν από την υποβολή.\n",
    "- Οι λανθασμένες αποκρίσεις της `conv2d_candidate` παραμένουν ως μέρος του παραδείγματος. \n",
    "- Ο κώδικας πρέπει να εκτελείται χωρίς σφάλματα και να παράγει σωστά αποτελέσματα. Αν ο κώδικας ενός ερωτήματος δεν εκτελείται, δεν αποδίδονται οι μονάδες του συγκεκριμένου ερωτήματος.\n",
    "- Βεβαιωθείτε ότι έχετε γράψει τα στοιχεία σας στην αρχή του notebook, στο κελί που ζητούνται.\n",
    "- Προαιρετικά, στο τελευταίο κελί μπορείτε να δώσετε σε 1-2 προτάσεις feedback σχετικά με την άσκηση. Το feedback μπορεί να αφορά: το περιεχόμενο, τον χρόνο που απαιτήθηκε για την ολοκλήρωση της, τον βαθμό δυσκολίας, ή ακόμη και κάποια συγκεκριμένη απορία πάνω στην άσκηση."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b13e4ba",
   "metadata": {},
   "source": [
    "### Προαιρετικά: Δώστε μας feedback!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fd9283f",
   "metadata": {},
   "source": [
    "Πόσο σας βοήθησε κάθε μέρος του φροντιστηρίου στην καταννόηση;\n",
    "\n",
    "Α. Οι αναλυτικές ασκήσεις\n",
    "\n",
    "Β. Η σύνδεση αναλυτικών ασκήσεων με την υλοποίηση σε python\n",
    "\n",
    "Γ. Το ελεύθερο πειραματικό μέρος\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "ccfa26ac",
   "metadata": {
    "ecey523": {
     "answer_id": "feedback_ratings"
    }
   },
   "outputs": [],
   "source": [
    "### TODO: ADD YOUR CODE HERE\n",
    "# Βαθμολογήστε από 0 (Καθόλου) έως 10 (Πάρα πολύ).\n",
    "\n",
    "feedback_A = None  # αναλυτικές ασκήσεις\n",
    "feedback_B = None  # σύνδεση αναλυτικών ασκήσεων με την υλοποίηση σε python\n",
    "feedback_C = None  # ελεύθερο πειραματικό μέρος\n",
    "### END of TODO\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e36311f",
   "metadata": {
    "ecey523": {
     "answer_id": "feedback_text"
    }
   },
   "source": [
    "_Μπορείτε να γράψετε εδώ ό,τι άλλα σχόλια έχετε._"
   ]
  }
 ],
 "metadata": {
  "ecey523": {
   "assignment": "hw1",
   "format_version": 2,
   "variant": "student"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
