Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tJj3j-004o34-EC for pgsql-hackers@arkaria.postgresql.org; Sat, 07 Dec 2024 00:52:35 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tJj3g-00Ec86-CD for pgsql-hackers@arkaria.postgresql.org; Sat, 07 Dec 2024 00:52:33 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tJj3f-00Ec7y-T6 for pgsql-hackers@lists.postgresql.org; Sat, 07 Dec 2024 00:52:33 +0000 Received: from mail-lj1-x235.google.com ([2a00:1450:4864:20::235]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1tJj3d-001R3s-0L for pgsql-hackers@lists.postgresql.org; Sat, 07 Dec 2024 00:52:32 +0000 Received: by mail-lj1-x235.google.com with SMTP id 38308e7fff4ca-30020d33d05so22734251fa.0 for ; Fri, 06 Dec 2024 16:52:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1733532748; x=1734137548; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=a+CX/qtDoRT2n2qmzTXMjtvirdB3SUmkLiCdgmO5sZ8=; b=OT/uPh5P9IJt+YY0kc/tvLxmoU55R1Qub5o0eq0mT1C8kzpOE5BAsJcpBftig34BBa DwfAjHCftUJ3R/1hKCrnK2Axk6WI0LXE/2Mkivhh7J0o+DPbtWVCydAIqclU82ay0yUb V5rwV4r0gzU71xGI5wBGWgbfeNU6ea7T4ngCeLE8XiApnOh/Ir94X80aRGh8ivHkZHqh nsB5vXr/fhGE2i7XEVxYMpjhAYO97wfC+ck4cHuC0y5hwSrFKorHAGOPcFEdcRXa1ZYn baCB1FazPOG4O//6JNsLiuSlkbkdnRjpgwFcrBWiblH2YSAJPfFRpX7lWDI5kdb2ncdI OVcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1733532748; x=1734137548; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=a+CX/qtDoRT2n2qmzTXMjtvirdB3SUmkLiCdgmO5sZ8=; b=RAw9uFVg4jZmdYi405xhMjSSv65GvDpfzk37ivaACNvs8ixJmaEKT9BZ0/tvkBT62S rVQMjdJeBX6KcPQjSMydm/GN20XRzdQBukqY/YZOl+jdNbe/Wn8Crid0rvt4RzYZ0B4G g1szuCbFMlnYh6UMhkZ6Cobz1XlA6w5onXSU2t2gG3SL/Ws0wAwUlZ2EnRFtHRS95LO4 blrrEqFgrxjl0J7ynSnO+M4xo6T+Nb2qdHq1cWBsYilREbnWxgRwp+0z/2n2BHUO3353 +jeTmr1grjXARIbaEL1zmgmufR+OhZAlw3jRLCYpAv5fYF4rywEJTwJ8fKE5vQwgltrE S2XA== X-Forwarded-Encrypted: i=1; AJvYcCVmjaoS7BkWU/dStctNOZs7rRpqjOaNnRx55Vez7LPau8wD5I/u+aLVi2cL+Os5pIZ8EDApIKeWwQiLeCvE@lists.postgresql.org X-Gm-Message-State: AOJu0YwrEJnL5dTcaGrKm2avH9YQAs2FJcY1vAG92DZeWFD7bxQkMa/N F1nAP4CfHi9/F/5xXwDBJ84JxM8wA0oQmeVZAb7cvsGdrMyGd0SIpm6haZCxNz5xVfN2TouDKzZ 6H3uMETIgBG+Mqe4yMEDJcRvqHmU= X-Gm-Gg: ASbGnct/YGfETTcH903uwA4N2PXS6LqQeASkimmQ+z+j+hkujeR73J2avmAagaXUVHx ejBeTQcdjCt9h9ybp+F6Iwdr1M3U4TX9f X-Google-Smtp-Source: AGHT+IFAoRGF3rqnnYQyrdD86WbG6FGmSrXTxGr3zDvyZRnap0pjYjXymHMW2HJQWbYBwAKAKL5PJGoN0k0kzi9TsFE= X-Received: by 2002:a2e:995a:0:b0:300:3307:389f with SMTP id 38308e7fff4ca-30033073c01mr10063351fa.0.1733532747258; Fri, 06 Dec 2024 16:52:27 -0800 (PST) MIME-Version: 1.0 References: <1342498.1729444411@sss.pgh.pa.us> <1445998.1729482404@sss.pgh.pa.us> <2062830.1729625620@sss.pgh.pa.us> <2265411.1729699470@sss.pgh.pa.us> <2354718.1729737539@sss.pgh.pa.us> <2581216.1729794746@sss.pgh.pa.us> <1948345.1730500073@sss.pgh.pa.us> <3797606.1732045516@sss.pgh.pa.us> <2234661.1733272923@sss.pgh.pa.us> In-Reply-To: <2234661.1733272923@sss.pgh.pa.us> From: Michel Pelletier Date: Fri, 6 Dec 2024 16:51:50 -0800 Message-ID: Subject: Re: Using Expanded Objects other than Arrays from plpgsql To: Tom Lane Cc: Pavel Stehule , pgsql-hackers@lists.postgresql.org Content-Type: multipart/alternative; boundary="0000000000009b640a0628a38b3a" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000009b640a0628a38b3a Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Tue, Dec 3, 2024 at 4:42=E2=80=AFPM Tom Lane wrote: > Michel Pelletier writes: > > Here's a WIP patch for a pgexpanded example in src/test/modules. > > I didn't look at your patch yet, but in the meantime here's an update > that takes the next step towards what I promised. > Awesome! I made a support function for my set_element(matrix, i, j, value) function and it works great, but I do have a couple questions about how to move forward with some of my other methods. For reference, here's the set_element test function, and a trace of function calls, after implementing the support function for set_element, the expand_matrix function is called twice, first by nvals(), and later by the first set_element, but not the subsequent sets, so the true clause of the PLPGSQL_RWOPT_INPLACE case in plpgsql_param_eval_var_check works great and as expected: postgres=3D# create or replace function test_se(graph matrix) returns matri= x language plpgsql as $$ declare nvals bigint; begin graph =3D wait(graph); nvals =3D nvals(graph); raise notice 'nvals: %', nvals; graph =3D set_element(graph, 4, 2, 42); graph =3D set_element(graph, 4, 3, 43); graph =3D set_element(graph, 4, 4, 44); return graph; end; $$; CREATE FUNCTION postgres=3D# select nvals(test_se('int32'::matrix)); DEBUG: new_matrix DEBUG: matrix_get_flat_size DEBUG: flatten_matrix DEBUG: matrix_wait DEBUG: DatumGetMatrix DEBUG: expand_matrix <- wait expands with support function DEBUG: new_matrix DEBUG: matrix_nvals DEBUG: DatumGetMatrix DEBUG: matrix_get_flat_size DEBUG: flatten_matrix DEBUG: expand_matrix <- nvals() reexpands DEBUG: new_matrix DEBUG: context_callback_matrix_free NOTICE: nvals: 0 DEBUG: scalar_int32 DEBUG: new_scalar DEBUG: matrix_set_element DEBUG: DatumGetMatrix < set_element does not reexpand, yay! DEBUG: DatumGetScalar DEBUG: context_callback_scalar_free DEBUG: scalar_int32 DEBUG: new_scalar DEBUG: matrix_set_element DEBUG: DatumGetMatrix DEBUG: DatumGetScalar DEBUG: context_callback_scalar_free DEBUG: scalar_int32 DEBUG: new_scalar DEBUG: matrix_set_element DEBUG: DatumGetMatrix DEBUG: DatumGetScalar DEBUG: context_callback_scalar_free DEBUG: matrix_nvals DEBUG: DatumGetMatrix DEBUG: context_callback_matrix_free DEBUG: context_callback_matrix_free =E2=94=8C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=90 =E2=94=82 nvals =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=A4 =E2=94=82 3 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=98 My question is about nvals =3D nvals(graph) in that function above, the object is flattened and then rexpanded, even after the object was expanded and returned by wait() [1] into a r/w pointer. set_element honors the expanded object from wait(), but nvals does not. It seems like I want to be able to pass the argument as a RO pointer, but I'm not sure how to trigger the "else" clause in the PLPGSQL_RWOPT_INPLACE case. I can see how the support function triggers the if, but I don't see a similar way to trigger the else. I'm almost certainly missing something. [1](Due to the asynchronous nature of the GraphBLAS API, there is a GrB_wait() function to wait until an object is "complete" computationally, but since this object was just expanded, there is no pending work, so the wait() is essentially a noop but a handy way for me to return a r/w pointer for subsequent operations). set_element and wait are the simple case where there is only one reference to the r/w object in the argument list. As we've discussed I have many functions that can possibly have multiple references to the same object. The core operation of the library is matrix multiplication, and all other complex functions follow a very similar pattern, so I'll just focus on that one here: CREATE FUNCTION mxm( a matrix, b matrix, op semiring default null, inout c matrix default null, mask matrix default null, accum binaryop default null, descr descriptor default null ) RETURNS matrix The order of arguments mostly follows the order in the C API. a and b are the left and right matrix operands and the matrix product is the return value. If c is not null, then it is a pre-created return value which may contain partial results already from some previous operations, otherwise mxm creates a new matrix of the correct dimensions and returns that. I think the inout is meaningless as it doesn't seem to change anything, but I'm using it as a visual indication in code that c can be the return value if it's not null. Here's an example of doing Triangle Counting using Burkhardt's method [2], where a, b, and the mask are all the same adjacency matrix (here the Newman/karate graph. The 'plus_pair' semiring is optimized for structural counting, and the descriptor 's' tells suitesparse to only use the structure of the mask and not to consider the values): [2] https://doi.org/10.1177/1473871616666393 CREATE OR REPLACE FUNCTION public.tcount_burkhardt(graph matrix) RETURNS bigint LANGUAGE plpgsql AS $$ begin graph =3D wait(graph); graph =3D mxm(graph, graph, 'plus_pair_int32', mask=3D>graph, descr=3D>'s'); return reduce_scalar(graph) / 6; end; $$; postgres=3D# select tcount_burkhardt(graph) from karateg; DEBUG: matrix_wait DEBUG: DatumGetMatrix DEBUG: expand_matrix <- wait expands and returns r/w pointer with support function DEBUG: new_matrix DEBUG: matrix_mxm <- mxm starts here DEBUG: DatumGetMatrix DEBUG: matrix_get_flat_size DEBUG: flatten_matrix DEBUG: expand_matrix <- expanding left operand again DEBUG: new_matrix DEBUG: DatumGetMatrix DEBUG: matrix_get_flat_size DEBUG: flatten_matrix DEBUG: expand_matrix <- expanding right operand again DEBUG: new_matrix DEBUG: DatumGetSemiring DEBUG: expand_semiring DEBUG: new_semiring DEBUG: new_matrix DEBUG: DatumGetMatrix DEBUG: matrix_get_flat_size DEBUG: flatten_matrix DEBUG: expand_matrix <- expanding mask argument again DEBUG: new_matrix DEBUG: DatumGetDescriptor DEBUG: expand_descriptor DEBUG: new_descriptor DEBUG: context_callback_matrix_free DEBUG: context_callback_descriptor_free DEBUG: context_callback_matrix_free DEBUG: context_callback_semiring_free DEBUG: context_callback_matrix_free DEBUG: context_callback_matrix_free DEBUG: matrix_reduce_scalar DEBUG: DatumGetMatrix DEBUG: matrix_get_flat_size DEBUG: flatten_matrix DEBUG: expand_matrix <- reduce also re-expands matrix DEBUG: new_matrix DEBUG: new_scalar DEBUG: scalar_div_int32 DEBUG: DatumGetScalar DEBUG: new_scalar DEBUG: cast_scalar_int64 DEBUG: DatumGetScalar DEBUG: context_callback_scalar_free DEBUG: context_callback_scalar_free DEBUG: context_callback_matrix_free DEBUG: context_callback_matrix_free =E2=94=8C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=90 =E2=94=82 tcount_burkhardt =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=A4 =E2=94=82 45 =E2=94=82 =E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=98 mxm calls expand_matrix three times for each of the three arguments. Ideally I'd like the already expanded rw pointer from wait() to be honored by mxm so that it doesn't re-expand the object three times but, like set_element, not at all. Hope that question makes sense, still going on the main theory that I'm not understanding the support function, and maybe the c argument thing being optional throws a wrench in the plan, and I'm happy to try and find a workaround for that. Maybe always requiring the result to be pre-constructed and then making c required and reassigning back to the same input argument is the right approach? Some good news I always like seeing is that 45 is the right answer. Very close to having optimal sparse linear algebra in Postgres! Thanks for your help! I'll move onto your comments on the test module next= . -Michel --0000000000009b640a0628a38b3a Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
On Tue, Dec 3, 2024 at 4:42=E2=80=AFPM To= m Lane <tgl@sss.pgh.pa.us> w= rote:
Michel Pelletier <pelletier.michel@gmail.com> writes:
> Here's a WIP patch for a pgexpanded example in src/test/modules.
I didn't look at your patch yet, but in the meantime here's an upda= te
that takes the next step towards what I promised.

=
Awesome!=C2=A0 I made a support function for my set_element(matr= ix, i, j, value) function and it works great, but I do have a couple=C2=A0q= uestions about how to move forward with some of my other methods.=C2=A0 For= reference, here's the set_element test function, and a trace of functi= on calls, after implementing the support function for set_element, the expa= nd_matrix function is called twice, first by nvals(), and later by the firs= t set_element, but not the subsequent sets, so the true clause of the=C2=A0= PLPGSQL_RWOPT_INPLACE case in=C2=A0plpgsql_param_eval_var_check works grea= t and as expected:

postgres=3D# create or replace = function test_se(graph matrix) returns matrix language plpgsql as
=C2=A0= =C2=A0 $$
=C2=A0 =C2=A0 declare
=C2=A0 =C2=A0 =C2=A0 =C2=A0 nvals bi= gint;
=C2=A0 =C2=A0 begin
=C2=A0 =C2=A0 =C2=A0 =C2=A0 graph =3D wait(= graph);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 nvals =3D nvals(graph);
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 raise notice 'nvals: %', nvals;
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 graph =3D set_element(graph, 4, 2, 42);
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 graph =3D set_element(graph, 4, 3, 43);
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 graph =3D set_element(graph, 4, 4, 44);
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 return graph;
=C2=A0 =C2=A0 end;
=C2=A0 =C2=A0 $$;
CREATE F= UNCTION
postgres=3D# select nvals(test_se('int32'::matrix));
= DEBUG: =C2=A0new_matrix
DEBUG: =C2=A0matrix_get_flat_size
DEBUG: =C2= =A0flatten_matrix
DEBUG: =C2=A0matrix_wait
DEBUG: =C2=A0DatumGetMatri= x
DEBUG: =C2=A0expand_matrix=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &l= t;- wait expands with support function
DEBUG: =C2=A0new_matrix
DEBUG:= =C2=A0matrix_nvals
DEBUG: =C2=A0DatumGetMatrix
DEBUG: =C2=A0matrix_g= et_flat_size
DEBUG: =C2=A0flatten_matrix
DEBUG: =C2=A0expand_matrix= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 <- nvals() reexpands
DEBUG:= =C2=A0new_matrix
DEBUG: =C2=A0context_callback_matrix_free
NOTICE: = =C2=A0nvals: 0
DEBUG: =C2=A0scalar_int32
DEBUG: =C2=A0new_scalar
D= EBUG: =C2=A0matrix_set_element
DEBUG: =C2=A0DatumGetMatrix=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 < set_element does not reexpand, yay!
DEB= UG: =C2=A0DatumGetScalar
DEBUG: =C2=A0context_callback_scalar_free
DE= BUG: =C2=A0scalar_int32
DEBUG: =C2=A0new_scalar
DEBUG: =C2=A0matrix_s= et_element
DEBUG: =C2=A0DatumGetMatrix
DEBUG: =C2=A0DatumGetScalarDEBUG: =C2=A0context_callback_scalar_free
DEBUG: =C2=A0scalar_int32
= DEBUG: =C2=A0new_scalar
DEBUG: =C2=A0matrix_set_element
DEBUG: =C2=A0= DatumGetMatrix
DEBUG: =C2=A0DatumGetScalar
DEBUG: =C2=A0context_callb= ack_scalar_free
DEBUG: =C2=A0matrix_nvals
DEBUG: =C2=A0DatumGetMatrix=
DEBUG: =C2=A0context_callback_matrix_free
DEBUG: =C2=A0context_callb= ack_matrix_free
=E2=94=8C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=90
=E2=94=82 nvals =E2=94=82
=E2=94=9C=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=A4
=E2= =94=82 =C2=A0 =C2=A0 3 =E2=94=82
=E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=98

=C2=A0M= y question is about nvals =3D nvals(graph) in that function above, the obje= ct is flattened and then rexpanded, even after the object was expanded and = returned by wait() [1] into a r/w pointer.=C2=A0 set_element honors the exp= anded object from wait(), but nvals does not.=C2=A0 It seems like I want to= be able to pass the argument as a RO pointer, but I'm not sure how to = trigger the "else" clause in the PLPGSQL_RWOPT_INPLACE case.=C2= =A0 I can see how the support function triggers the if, but I don't see= a similar way to trigger the else.=C2=A0 I'm almost certainly missing = something.

[1](Due to the asynchronous nature of t= he GraphBLAS API, there is a GrB_wait() function to wait until an object is= "complete" computationally, but since this object was just expan= ded, there is no pending work, so the wait() is essentially a noop but a ha= ndy way for me to return a r/w pointer for subsequent operations).

set_element and wait are the simple case where there is on= ly one reference to the r/w object in the argument list.=C2=A0 As we've= discussed I have many functions that can possibly have multiple references= to the same object.=C2=A0 The core operation of the library is matrix mult= iplication, and all other complex functions follow a very similar pattern, = so I'll just focus on that one here:

CREATE FU= NCTION mxm(
=C2=A0 =C2=A0 a matrix,
=C2=A0 =C2=A0 b matrix,
=C2=A0= =C2=A0 op semiring default null,
=C2=A0 =C2=A0 inout c matrix default n= ull,
=C2=A0 =C2=A0 mask matrix default null,
=C2=A0 =C2=A0 accum bina= ryop default null,
=C2=A0 =C2=A0 descr descriptor default null
=C2=A0= =C2=A0 )
RETURNS matrix

The order of arguments= mostly follows the order in the C API.=C2=A0 a and b are the left and righ= t matrix operands and the matrix product is the return value.=C2=A0 If c is= not null, then it is a pre-created return value which may contain partial = results already from some previous operations, otherwise mxm creates a new = matrix of the correct dimensions and returns that.=C2=A0 I think the inout = is meaningless as it doesn't seem to change anything, but I'm using= it as a visual indication in code that c can be the return value if it'= ;s not null.

Here's an example of doing Triang= le Counting using Burkhardt's method [2], where a, b, and the mask are = all the same adjacency matrix (here the Newman/karate graph.=C2=A0 The '= ;plus_pair' semiring is optimized for structural counting, and the desc= riptor 's' tells suitesparse to only use the structure of the mask = and not to consider the values):


CREATE OR REPLACE FUNCTION public.= tcount_burkhardt(graph matrix)
=C2=A0RETURNS bigint
=C2=A0LANGUAGE pl= pgsql
AS $$
=C2=A0 =C2=A0 begin
=C2=A0 =C2=A0 =C2=A0 =C2=A0 graph = =3D wait(graph);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 graph =3D mxm(graph, graph,= 'plus_pair_int32', mask=3D>graph, descr=3D>'s');
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 return reduce_scalar(graph) / 6;
=C2=A0 =C2= =A0 end;
=C2=A0 =C2=A0 $$;

postgres=3D# select tcount_= burkhardt(graph) from karateg;
DEBUG: =C2=A0matrix_wait
DEBUG: =C2=A0= DatumGetMatrix
DEBUG: =C2=A0expand_matrix=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 <- wait expands and returns r/w p= ointer with support function
DEBUG: =C2=A0new_matrix
DEBUG: =C2=A0mat= rix_mxm=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 <- mxm starts here
DEBUG: =C2=A0DatumGetMatrix
D= EBUG: =C2=A0matrix_get_flat_size
DEBUG: =C2=A0flatten_matrix
DEBUG: = =C2=A0expand_matrix=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 <- expanding left operand again
DEBUG: =C2=A0new_matrix=
DEBUG: =C2=A0DatumGetMatrix
DEBUG: =C2=A0matrix_get_flat_size
DEB= UG: =C2=A0flatten_matrix
DEBUG: =C2=A0expand_matrix=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 <- expanding right oper= and again
DEBUG: =C2=A0new_matrix
DEBUG: =C2=A0DatumGetSemiring
DE= BUG: =C2=A0expand_semiring=C2=A0
DEBUG: =C2=A0new_semiring
DEBUG: =C2= =A0new_matrix
DEBUG: =C2=A0DatumGetMatrix
DEBUG: =C2=A0matrix_get_fla= t_size
DEBUG: =C2=A0flatten_matrix
DEBUG: =C2=A0expand_matrix=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 <- expand= ing mask argument again
DEBUG: =C2=A0new_matrix
DEBUG: =C2=A0DatumGet= Descriptor
DEBUG: =C2=A0expand_descriptor
DEBUG: =C2=A0new_descriptor=
DEBUG: =C2=A0context_callback_matrix_free
DEBUG: =C2=A0context_callb= ack_descriptor_free
DEBUG: =C2=A0context_callback_matrix_free
DEBUG: = =C2=A0context_callback_semiring_free
DEBUG: =C2=A0context_callback_matri= x_free
DEBUG: =C2=A0context_callback_matrix_free
DEBUG: =C2=A0matrix_= reduce_scalar
DEBUG: =C2=A0DatumGetMatrix
DEBUG: =C2=A0matrix_get_fla= t_size
DEBUG: =C2=A0flatten_matrix
DEBUG: =C2=A0expand_matrix=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 <- reduce al= so re-expands matrix
DEBUG: =C2=A0new_matrix
DEBUG: =C2=A0new_scalar<= br>DEBUG: =C2=A0scalar_div_int32
DEBUG: =C2=A0DatumGetScalar
DEBUG: = =C2=A0new_scalar
DEBUG: =C2=A0cast_scalar_int64
DEBUG: =C2=A0DatumGet= Scalar
DEBUG: =C2=A0context_callback_scalar_free
DEBUG: =C2=A0context= _callback_scalar_free
DEBUG: =C2=A0context_callback_matrix_free
DEBUG= : =C2=A0context_callback_matrix_free
=E2=94=8C=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=90=E2=94=82 tcount_burkhardt=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =E2=94=82
=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=A4
=E2=94=82 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 45=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =E2=94=82
=E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=98

= mxm calls expand_matrix three times for each of the three arguments.=C2=A0 = Ideally I'd like the already expanded rw pointer from wait() to be hono= red by mxm so that it doesn't re-expand=C2=A0the object three times but= , like set_element, not at all.

Hope that question= makes sense, still going on the main theory that I'm not understanding= the support function, and maybe the c argument thing being optional throws= a wrench in the plan, and I'm happy to try and find a workaround for t= hat.=C2=A0 Maybe always requiring the result to be pre-constructed and then= making c required and reassigning back to the same input argument is the r= ight approach?

Some good news I always like seeing= is that 45 is the right answer.=C2=A0 Very close to having optimal sparse = linear algebra in Postgres!

Thanks for your help!= =C2=A0 I'll move onto your comments on the test module next.
=
-Michel
=C2=A0=C2=A0
--0000000000009b640a0628a38b3a--