1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
|
//------------------------------------------------------------------------------
// GB_Monoid_new: create a GrB_Monoid
//------------------------------------------------------------------------------
// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2022, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
// Create a monoid with an operator, (optionally) an identity value, and
// (optionally) a terminal value. If using a built-in operator, a duplicate
// boolean operator is first replaced with its unique equivalent. If the
// operator is built-in and corresponds to a known monoid, then the identity
// value and terminal value provided on input are silently ignored, and the
// known values are used instead. This is to allow the use of the hard-coded
// functions for built-in monoids.
// User-defined monoids may have a NULL terminal value, which denotes that the
// monoid does not have a terminal value.
#include "GB.h"
#include "GB_binop.h"
#include "GB_Monoid_new.h"
GrB_Info GB_Monoid_new // create a monoid
(
GrB_Monoid *monoid, // handle of monoid to create
GrB_BinaryOp op, // binary operator of the monoid
const void *identity, // identity value, if any
const void *terminal, // terminal value, if any (may be NULL)
GB_Type_code idcode, // identity and terminal type code
GB_Context Context
)
{
//--------------------------------------------------------------------------
// check inputs
//--------------------------------------------------------------------------
GB_RETURN_IF_NULL (monoid) ;
(*monoid) = NULL ;
GB_RETURN_IF_NULL (identity) ;
GB_RETURN_IF_NULL_OR_FAULTY (op) ;
ASSERT_BINARYOP_OK (op, "op for monoid", GB0) ;
ASSERT (idcode <= GB_UDT_code) ;
//--------------------------------------------------------------------------
// rename built-in binary operators
//--------------------------------------------------------------------------
// If the user requests the creation of a monoid based on a duplicate
// built-in binary operator, the unique boolean operator is used instead.
// See also GB_boolean_rename, which does this for opcodes, not operators.
op = GB_boolean_rename_op (op) ;
ASSERT_BINARYOP_OK (op, "revised op", GB0) ;
//--------------------------------------------------------------------------
// continue checking inputs
//--------------------------------------------------------------------------
// check operator types; all must be identical, and the operator cannot
// be positional
if (op->xtype != op->ztype || op->ytype != op->ztype ||
GB_OP_IS_POSITIONAL (op))
{
return (GrB_DOMAIN_MISMATCH) ;
}
// The idcode must match the monoid->op->ztype->code for built-in types,
// and this can be rigourously checked. For all user-defined types,
// identity is a mere void * pointer, and its actual type cannot be
// compared with the input op->ztype parameter. Only the type code,
// GB_UDT_code, can be checked to see if it matches. In
// that case, all that is known is that identity is a void * pointer that
// points to something; it must a scalar of the proper user-defined type.
GB_Type_code zcode = op->ztype->code ;
if (idcode != zcode)
{
return (GrB_DOMAIN_MISMATCH) ;
}
//--------------------------------------------------------------------------
// create the monoid
//--------------------------------------------------------------------------
// allocate the monoid
size_t header_size ;
(*monoid) = GB_MALLOC (1, struct GB_Monoid_opaque, &header_size) ;
if (*monoid == NULL)
{
// out of memory
return (GrB_OUT_OF_MEMORY) ;
}
// initialize the monoid
GrB_Monoid mon = *monoid ;
mon->magic = GB_MAGIC ;
mon->header_size = header_size ;
mon->op = op ;
size_t zsize = op->ztype->size ;
mon->identity = NULL ; // defined below (if present)
mon->terminal = NULL ; // defined below (if present)
mon->identity_size = 0 ;
mon->terminal_size = 0 ;
//--------------------------------------------------------------------------
// allocation of identity and terminal values
//--------------------------------------------------------------------------
// allocate the identity value
#define GB_ALLOC_IDENTITY \
{ \
mon->identity = GB_MALLOC (zsize, GB_void, &(mon->identity_size)) ; \
if (mon->identity == NULL) \
{ \
/* out of memory */ \
GB_FREE (&(mon->terminal), mon->terminal_size) ; \
GB_FREE (monoid, header_size) ; \
return (GrB_OUT_OF_MEMORY) ; \
} \
}
// allocate the terminal value
#define GB_ALLOC_TERMINAL \
{ \
mon->terminal = GB_MALLOC (zsize, GB_void, &(mon->terminal_size)) ; \
if (mon->terminal == NULL) \
{ \
/* out of memory */ \
GB_FREE (&(mon->identity), mon->identity_size) ; \
GB_FREE (monoid, header_size) ; \
return (GrB_OUT_OF_MEMORY) ; \
} \
}
//--------------------------------------------------------------------------
// create a user-defined monoid based on a known built-in monoid
//--------------------------------------------------------------------------
bool done = false ;
// If the user requests the creation of a monoid based on a built-in
// operator that corresponds to known built-in monoid, the identity and
// terminal values provided by the user to GrB_Monoid_new are ignored,
// since these are all handled by the same hard-coded functions as the
// built-in monoids based on the same operator.
// All of the monoids created in the switch statement below have non-NULL
// identity values; some have terminal values and some do not.
// create a monoid with both an identity and a terminal value,
// based on a built-in operator that is a known monoid
#define GB_IT(ztype,identity_value,terminal_value) \
{ \
GB_ALLOC_TERMINAL ; \
GB_ALLOC_IDENTITY ; \
ztype *identity = (ztype *) mon->identity ; \
ztype *terminal = (ztype *) mon->terminal ; \
(*identity) = identity_value ; \
(*terminal) = terminal_value ; \
done = true ; \
} \
break ;
// create a monoid with just an identity but no terminal value,
// based on a built-in operator that is a known monoid
#define GB_IN(ztype,identity_value) \
{ \
GB_ALLOC_IDENTITY ; \
ztype *identity = (ztype *) mon->identity ; \
(*identity) = identity_value ; \
done = true ; \
} \
break ;
switch (op->opcode)
{
case GB_MIN_binop_code :
// MIN monoid: identity is +inf, terminal is -inf
// note there is no MIN monoid for complex types
switch (zcode)
{
case GB_INT8_code : GB_IT (int8_t , INT8_MAX , INT8_MIN )
case GB_INT16_code : GB_IT (int16_t , INT16_MAX , INT16_MIN )
case GB_INT32_code : GB_IT (int32_t , INT32_MAX , INT32_MIN )
case GB_INT64_code : GB_IT (int64_t , INT64_MAX , INT64_MIN )
case GB_UINT8_code : GB_IT (uint8_t , UINT8_MAX , 0 )
case GB_UINT16_code : GB_IT (uint16_t, UINT16_MAX, 0 )
case GB_UINT32_code : GB_IT (uint32_t, UINT32_MAX, 0 )
case GB_UINT64_code : GB_IT (uint64_t, UINT64_MAX, 0 )
case GB_FP32_code : GB_IT (float , INFINITY , -INFINITY )
case GB_FP64_code : GB_IT (double , ((double) INFINITY) ,
((double) -INFINITY) )
default: ;
}
break ;
case GB_MAX_binop_code :
// MAX monoid: identity is -inf, terminal is +inf
// note there is no MAX monoid for complex types
switch (zcode)
{
case GB_INT8_code : GB_IT (int8_t , INT8_MIN , INT8_MAX )
case GB_INT16_code : GB_IT (int16_t , INT16_MIN , INT16_MAX )
case GB_INT32_code : GB_IT (int32_t , INT32_MIN , INT32_MAX )
case GB_INT64_code : GB_IT (int64_t , INT64_MIN , INT64_MAX )
case GB_UINT8_code : GB_IT (uint8_t , 0 , UINT8_MAX )
case GB_UINT16_code : GB_IT (uint16_t, 0 , UINT16_MAX)
case GB_UINT32_code : GB_IT (uint32_t, 0 , UINT32_MAX)
case GB_UINT64_code : GB_IT (uint64_t, 0 , UINT64_MAX)
case GB_FP32_code : GB_IT (float , -INFINITY , INFINITY )
case GB_FP64_code : GB_IT (double , ((double) -INFINITY) ,
((double) INFINITY) )
default: ;
}
break ;
case GB_PLUS_binop_code :
// PLUS monoid: identity is zero, no terminal value
switch (zcode)
{
case GB_INT8_code : GB_IN (int8_t , 0 )
case GB_INT16_code : GB_IN (int16_t , 0 )
case GB_INT32_code : GB_IN (int32_t , 0 )
case GB_INT64_code : GB_IN (int64_t , 0 )
case GB_UINT8_code : GB_IN (uint8_t , 0 )
case GB_UINT16_code : GB_IN (uint16_t, 0 )
case GB_UINT32_code : GB_IN (uint32_t, 0 )
case GB_UINT64_code : GB_IN (uint64_t, 0 )
case GB_FP32_code : GB_IN (float , 0 )
case GB_FP64_code : GB_IN (double , 0 )
case GB_FC32_code : GB_IN (GxB_FC32_t, GxB_CMPLXF(0,0) )
case GB_FC64_code : GB_IN (GxB_FC64_t, GxB_CMPLX(0,0) )
default: ;
}
break ;
case GB_TIMES_binop_code :
// TIMES monoid: identity is 1, no terminal value
switch (zcode)
{
case GB_INT8_code : GB_IN (int8_t , 1 )
case GB_INT16_code : GB_IN (int16_t , 1 )
case GB_INT32_code : GB_IN (int32_t , 1 )
case GB_INT64_code : GB_IN (int64_t , 1 )
case GB_UINT8_code : GB_IN (uint8_t , 1 )
case GB_UINT16_code : GB_IN (uint16_t, 1 )
case GB_UINT32_code : GB_IN (uint32_t, 1 )
case GB_UINT64_code : GB_IN (uint64_t, 1 )
case GB_FP32_code : GB_IN (float , 1 )
case GB_FP64_code : GB_IN (double , 1 )
case GB_FC32_code : GB_IN (GxB_FC32_t, GxB_CMPLXF(1,0) )
case GB_FC64_code : GB_IN (GxB_FC64_t, GxB_CMPLX(1,0) )
default: ;
}
break ;
case GB_ANY_binop_code :
// ANY monoid: identity is anything, terminal value is anything
switch (zcode)
{
case GB_BOOL_code : GB_IT (bool , 0, 0 )
case GB_INT8_code : GB_IT (int8_t , 0, 0 )
case GB_INT16_code : GB_IT (int16_t , 0, 0 )
case GB_INT32_code : GB_IT (int32_t , 0, 0 )
case GB_INT64_code : GB_IT (int64_t , 0, 0 )
case GB_UINT8_code : GB_IT (uint8_t , 0, 0 )
case GB_UINT16_code : GB_IT (uint16_t, 0, 0 )
case GB_UINT32_code : GB_IT (uint32_t, 0, 0 )
case GB_UINT64_code : GB_IT (uint64_t, 0, 0 )
case GB_FP32_code : GB_IT (float , 0, 0 )
case GB_FP64_code : GB_IT (double , 0, 0 )
case GB_FC32_code :
GB_IT (GxB_FC32_t, GxB_CMPLXF(0,0), GxB_CMPLXF(0,0))
case GB_FC64_code :
GB_IT (GxB_FC64_t, GxB_CMPLX(0,0), GxB_CMPLX(0,0))
default: ;
}
break ;
case GB_LOR_binop_code :
// boolean OR monoid: identity is false, terminal is true
if (zcode == GB_BOOL_code) GB_IT (bool, false, true)
case GB_LAND_binop_code :
// boolean AND monoid: identity is true, terminal is false
if (zcode == GB_BOOL_code) GB_IT (bool, true, false)
case GB_LXOR_binop_code :
// boolean XOR monoid: identity is false, no terminal value
if (zcode == GB_BOOL_code) GB_IN (bool, false)
case GB_EQ_binop_code :
// boolean EQ monoid: identity is true, no terminal value
if (zcode == GB_BOOL_code) GB_IN (bool, true)
case GB_BOR_binop_code :
// BOR monoids (bitwise or):
switch (zcode)
{
case GB_UINT8_code : GB_IT (uint8_t , 0, 0xFF )
case GB_UINT16_code : GB_IT (uint16_t, 0, 0xFFFF )
case GB_UINT32_code : GB_IT (uint32_t, 0, 0xFFFFFFFF )
case GB_UINT64_code : GB_IT (uint64_t, 0, 0xFFFFFFFFFFFFFFFF )
default: ;
}
break ;
case GB_BAND_binop_code :
// BAND monoids (bitwise and):
switch (zcode)
{
case GB_UINT8_code : GB_IT (uint8_t , 0xFF , 0 )
case GB_UINT16_code : GB_IT (uint16_t, 0xFFFF , 0 )
case GB_UINT32_code : GB_IT (uint32_t, 0xFFFFFFFF , 0 )
case GB_UINT64_code : GB_IT (uint64_t, 0xFFFFFFFFFFFFFFFF, 0 )
default: ;
}
break ;
case GB_BXOR_binop_code :
// BXOR monoids (bitwise xor): not terminal
switch (zcode)
{
case GB_UINT8_code : GB_IN (uint8_t , 0 )
case GB_UINT16_code : GB_IN (uint16_t, 0 )
case GB_UINT32_code : GB_IN (uint32_t, 0 )
case GB_UINT64_code : GB_IN (uint64_t, 0 )
default: ;
}
break ;
case GB_BXNOR_binop_code :
// BXNOR monoids (bitwise xnor): not terminal
switch (zcode)
{
case GB_UINT8_code : GB_IN (uint8_t , 0xFF )
case GB_UINT16_code : GB_IN (uint16_t, 0xFFFF )
case GB_UINT32_code : GB_IN (uint32_t, 0xFFFFFFFF )
case GB_UINT64_code : GB_IN (uint64_t, 0xFFFFFFFFFFFFFFFF )
default: ;
}
break ;
default : ;
// monoid identity and terminal value defined below
}
//--------------------------------------------------------------------------
// user-defined operators or unknown monoids
//--------------------------------------------------------------------------
// The monoid is based on a user-defined operator, or on a built-in
// operator that is not a known monoid. Use the identity and terminal
// values provided on input.
if (!done)
{
// create the monoid identity value
GB_ALLOC_IDENTITY ;
memcpy (mon->identity, identity, zsize) ;
if (terminal != NULL)
{
// create the monoid terminal value
GB_ALLOC_TERMINAL ;
memcpy (mon->terminal, terminal, zsize) ;
}
}
ASSERT_MONOID_OK (mon, "new monoid", GB0) ;
return (GrB_SUCCESS) ;
}
|