25) HW3 Review#
Today#
Review of HW3
Submission expectations and reminders
1. Review of HW3#
Solution to HW3. Here is my C code:
1
2#include <argp.h>
3#include <math.h>
4#include <omp.h>
5#include <stdbool.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10struct Args {
11 size_t length;
12 size_t nreps;
13 bool block;
14 size_t unroll_factor;
15};
16
17// static memory variable declared here
18static struct argp_option options[] = {
19 {"length", 'S', "size_t", 0, "Length of each vector"},
20 {"nreps", 'r', "size_t", 0, "Number of repetitions"},
21 {"block", 'b', NULL, 0, "Compute block dot products (versus a single dot product)"}
22};
23
24static error_t parse_opt (int key, char *arg, struct argp_state *state)
25{
26 struct Args *args = state->input;
27 switch (key) {
28 case ARGP_KEY_INIT:
29 args->length = 24;
30 args->nreps = 10;
31 args->block = false;
32 break;
33 case 'S':
34 args->length = strtol(arg, NULL, 10);
35 break;
36 case 'r':
37 args->nreps = strtol(arg, NULL, 10);
38 break;
39 case 'b':
40 args->block = true;
41 break;
42 default:
43 return ARGP_ERR_UNKNOWN;
44 }
45 return 0;
46}
47
48int I = 8;
49int J = 4;
50double tol = 1e-6;
51
52int c_length[] = {3, 4};
53
54
55// Function declarations here
56double dot_product(int S, const double *a, const double *b);
57double vector_norm(const double *a, int S);
58int is_orthogonal(int S, const double *a, const double *b, double tol);
59void bdot(int I, int S, int J, const double *m, const double *n, double *p);
60
61double dot_product(int S, const double *a, const double *b){
62
63 double sum = 0;
64 for (int i = 0; i<S; i++)
65 sum += a[i] * b[i];
66
67 return sum;
68}
69
70double vector_norm(const double *a, int S){
71
72 double sum = 0;
73 for (int i = 0; i<S; i++)
74 sum += pow(a[i],2);
75
76 return sqrt(sum);
77}
78
79int is_orthogonal(int S, const double *a, const double *b, double tol){
80
81 return (fabs(dot_product(S, a, b) <= tol));
82 }
83
84// Performs the operation
85// P = M * N
86// where M and N have shape (I,S) and (S,J) respectively.
87// This version stores M as row-major and N as column-major.
88void bdot(int I, int S, int J, const double *m, const double *n, double *p){
89
90 for (int i=0; i<I; i++) {
91 for (int j=0; j<J; j++) {
92 p[i*J+j] = dot_product(S, &m[i*S], &n[j*S]);
93 }
94 }
95}
96
97static void init_bdot(int I, int S, int J, double *m, double *n) {
98 for (int s=0; s<S; s++) {
99 for (int i=0; i<I; i++)
100 m[i*S + s] = 1000*(i+1) + s+1;
101 for (int j=0; j<J; j++)
102 n[j*S + s] = 1./(1000*(j+1) + s+1);
103 }
104}
105
106
107// Reference matrix-matrix multiply product implementation
108void matrix_ref(int I, int S, int J, const double *m, const double *n, double *p){
109 for (int i=0; i<I; i++) {
110 for (int j=0; j<J; j++) {
111 p[i*J+j] = 0.0;
112 for (int s=0; s<S; s++) {
113 p[i*J+j] += m[i*S + s] * n[j*S + s];
114 }
115 }
116 }
117}
118
119static void report_dot(const char *name, const double result, const double ref_result) {
120
121 if (fabs(result - ref_result) > 1e-10) {
122 printf("Result = %f failed to validate with expected value %f\n", result, ref_result);
123 return;
124 }
125 printf("%s matches the reference result.\n", name);
126}
127
128static void report_is_orthogonal(const char *name, const int result, const int ref_result) {
129 if (result != ref_result) {
130 printf("Result = %d failed to validate with expected value %d \n", result, ref_result);
131 return;
132 }
133 printf("%s matches the reference result. The two vectors are %s \n", name, ref_result ? "orthogonal.": "not orthogonal.");
134}
135
136static void report_vector_norm(const char *name, const double result, const double ref_result) {
137
138 if (fabs(result - ref_result) > 1e-10) {
139 printf("Result = %f failed to validate with expected value %f\n", result, ref_result);
140 return;
141 }
142 printf("%s matches the reference result.\n", name);
143}
144
145static void report_bdot(const char *name, int I, int J, const double *result, const double *ref_result) {
146 if (result && ref_result && result != ref_result) {
147 for (int i=0; i<I; i++) {
148 for (int j=0; j<J; j++) {
149 if (fabs(result[i*J + j] - ref_result[i*J + j]) > 1e-10) {
150 printf("Result[%d,%d] = %f failed to validate with expected value %f\n", i, j, result[i*J + j], ref_result[i*J + j]);
151 return;
152 }
153 }
154 }
155 }
156 printf("%s matches the reference result.\n", name);
157}
158
159
160#define REPORT_BDOT(f, I, S, J, m, n, p, p_ref) do { \
161 f(I, S, J, m, n, p); \
162 report_bdot(#f, I, J, p, p_ref); \
163} while (0)
164
165int main(int argc, char **argv){
166
167 struct Args args;
168 struct argp argp = {options, parse_opt, NULL, NULL};
169 argp_parse(&argp, argc, argv, 0, 0, &args);
170 size_t S = args.length;
171
172 switch (args.block) {
173 case false: { // single dot product case
174 // stack memory variable declarations here
175 double a1[] = {1, 0, 0};
176 double b1[] = {0, 1, 0};
177 double a2[] = {1, 2, 3, 4};
178 double b2[] = {1, 1, 1, 1};
179
180 // result of (a1,b1) and (a2,b2) dot products and reference values
181 double c1, c2;
182 double c1_ref = 0;
183 double c2_ref = 10;
184
185 // result of is_orthogonal
186 int flag1, flag2;
187 int flag1_ref = 1; // a1 and b1 are orthogonal
188 int flag2_ref = 0; // a2 and b2 are not orthogonal
189
190 // result for vector_norm
191 double na1, na2, nb1, nb2;
192 double na1_ref = 1.0;
193 double na2_ref = 5.477225575051661;
194 double nb1_ref = 1.0;
195 double nb2_ref = 2.0;
196
197 // calls to your functions by reference here
198 // print statements to show results
199 c1 = dot_product(c_length[0], a1, b1);
200 report_dot("dot_product of a1 and b1", c1, c1_ref);
201 c2 = dot_product(c_length[1], a2, b2);
202 report_dot("dot_product of a2 and b2", c2, c2_ref);
203
204 flag1 = is_orthogonal(c_length[0], a1, b1, tol);
205 report_is_orthogonal("is_orthogonal between a1 and b1", flag1, flag1_ref);
206 flag2 = is_orthogonal(c_length[1], a2, b2, tol);
207 report_is_orthogonal("is_orthogonal between a2 and b2", flag2, flag2_ref);
208
209 na1 = vector_norm(a1, c_length[0]);
210 nb1 = vector_norm(b1, c_length[0]);
211 na2 = vector_norm(a2, c_length[1]);
212 nb2 = vector_norm(b2, c_length[1]);
213 report_vector_norm("vector_norm of a1", na1, na1_ref);
214 report_vector_norm("vector_norm of a2", na2, na2_ref);
215 report_vector_norm("vector_norm of b1", nb1, nb1_ref);
216 report_vector_norm("vector_norm of b2", nb2, nb2_ref);
217
218 } break;
219 case true: { // blocked dot product case
220 // Initialize the matrices (as flattened vectors)
221 // heap memory allocations here
222 double *m = malloc(I * S * sizeof(double));
223 double *n = malloc(J * S * sizeof(double));
224 double *p = malloc(I * J * sizeof(double));
225 double *p_ref = malloc(I * J * sizeof(double));
226
227 init_bdot(I, args.length, J, m, n);
228 matrix_ref(I, S, J, m, n, p_ref);
229 REPORT_BDOT(bdot, I, S, J, m, n, p, p_ref);
230
231 // free allocated heap memory here
232 free(m); free(n); free(p); free(p_ref);
233 } break;
234 }
235
236 return 0;
237}
238
This program can be compiled with the GNU C compiler with:
gcc dot.c -lm -o dot
and executed with
./dot
or
./dot -b
to test the blocked version.
Common mistakes#
Here is a list of common mistakes that a few people made:
Part 4: Not really implementing a blocked dot product between the rows of \(M\) and the columns of \(N\). The best way would have been reusing the
dot_productfunction that was implemented for Part 1. This allows for code re-use, which is a good practice.E.C.: not safely initializing to zero the output variable for the triple nested loop matrix-matrix multiplication. You could have achieved this by either invoking
callocinstead ofmallocor by manually initializing to zero all entries of the output variable (typically this is done right before the inner-most loop).
2. Submission expectations and reminders#
In general, when you receive an Assignment via a GitHub Classroom link, you want to clone your assignment repository, by doing
git clone your_assignment_repository_url
You can also work on a back-up repository or directory of your choice if you want to, for your scrap work, but you have to clone the assignment repository and submit your work there to be considered for submission and grading.
As soon as you clone your Assignment repository, move to that repository
cd your_assignment_repository
Create a new feature branch and switch to that. You can do this in two ways:
git checkout -b name_of_your_branchgit branch name_of_your_branchand thengit checkout name_of_your_branch
Do NOT work directly off
mainYou can work on your feature branch as much as you like and create repeated incremental snapshots of your work via
git commit. Always remember to use meaningful commit messages to remind yourself (and others) about your work in that moment in time. In a terminal you can simply do this by
git commit -m "Your commit message"
You can also write multi-line more detailed commit messages if you want. Just simply separate them with a space, and repeat the -m option, as in:
git commit -m "Your commit message" -m "Your more detailed message on a new line"
When you are satisfied with your committed work, you can push it to your working branch via:
git push origin your_branch_name
If it is the first time you are doing this, git will automatically tell you that you can open a Pull Request with your changes. Just CTRL-click on the URL that git shows you in the terminal and you will be sent to your Pull Request web interface.
Any successive changes that you want to push to your branch, they will be automatically reflected on the open PR.
Only changes made within the deadline (including the lateness window) will be graded.
Remember not to attempt to close or merge your PR without any Reviewer (in this case your instructor) approval.
Always remember to double check the
File changedtab in your PR. If you see files that should not belong there (e.g., files automatically created by your IDE or virtual environment files) remove them.If you are using an IDE that automatically creates hidden project files that you might inadvertently push to your branch, it is always a good practice to use a
.gitignorefile that specify which files you do not want to be tracked bygit, and therefore, pushed to your branch. Recall that we covered this in our first lecture.
Reminder about the AI policy in this course#
A friendly reminder that in this course, we follow the University Senate’s extended definition of plagiarism that includes the un-cited use of generative AI applications, specifically: “representing work produced by generative Artificial Intelligence as one’s own.”
I provided in the Syllabus examples of how to properly cite the use of any genAI or LLM tool.