Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: change encoding of variable and lookup indices to allow for arbitrary number of subscripts #507

Merged
merged 1 commit into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 12 additions & 22 deletions packages/cli/src/c/model.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@
struct timespec startTime, finishTime;
#endif

// For each output variable specified in the indices buffer, there
// are 4 index values:
// varIndex
// subIndex0
// subIndex1
// subIndex2
#define INDICES_PER_OUTPUT 4

// The special _time variable is not included in .mdl files.
double _time;

Expand Down Expand Up @@ -149,22 +141,20 @@ void run() {
}
outputVarIndex = 0;
if (outputIndexBuffer != NULL) {
// Store the outputs as specified in the current output index buffer. This
// iterates over the output indices buffer until we reach the first zero index.
size_t i = 0;
while (true) {
size_t indexBufferOffset = i * INDICES_PER_OUTPUT;
size_t varIndex = (size_t)outputIndexBuffer[indexBufferOffset];
if (varIndex > 0) {
size_t subIndex0 = (size_t)outputIndexBuffer[indexBufferOffset + 1];
size_t subIndex1 = (size_t)outputIndexBuffer[indexBufferOffset + 2];
size_t subIndex2 = (size_t)outputIndexBuffer[indexBufferOffset + 3];
storeOutput(varIndex, subIndex0, subIndex1, subIndex2);
// Store the outputs as specified in the current output index buffer
size_t indexBufferOffset = 0;
size_t outputCount = (size_t)outputIndexBuffer[indexBufferOffset++];
for (size_t i = 0; i < outputCount; i++) {
size_t varIndex = (size_t)outputIndexBuffer[indexBufferOffset++];
size_t subCount = (size_t)outputIndexBuffer[indexBufferOffset++];
size_t* subIndices;
if (subCount > 0) {
subIndices = (size_t*)(outputIndexBuffer + indexBufferOffset);
} else {
// Stop when we reach the first zero index
break;
subIndices = NULL;
}
i++;
indexBufferOffset += subCount;
storeOutput(varIndex, subIndices);
}
} else {
// Store the normal outputs
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/c/sde.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ void initConstants(void);
void initLevels(void);
void setInputs(const char* inputData);
void setInputsFromBuffer(double *inputData);
void setLookup(size_t varIndex, size_t* subIndices, double* points, size_t numPoints);
void evalAux(void);
void evalLevels(void);
void storeOutputData(void);
void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t subIndex2);
void storeOutput(size_t varIndex, size_t* subIndices);
const char* getHeader(void);

#ifdef __cplusplus
Expand Down
12 changes: 3 additions & 9 deletions packages/compile/src/generate/gen-code-c.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ void storeOutputData() {
${specOutputSection(outputVarIds)}
}

void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t subIndex2) {
void storeOutput(size_t varIndex, size_t* subIndices) {
${storeOutputBody}
}
`
Expand Down Expand Up @@ -396,14 +396,8 @@ ${section(chunk)}
})
const code = R.map(info => {
let varAccess = info.varName
if (info.subscriptCount > 0) {
varAccess += '[subIndex0]'
}
if (info.subscriptCount > 1) {
varAccess += '[subIndex1]'
}
if (info.subscriptCount > 2) {
varAccess += '[subIndex2]'
for (let i = 0; i < info.subscriptCount; i++) {
varAccess += `[subIndices[${i}]]`
}
return `\
case ${info.varIndex}:
Expand Down
14 changes: 7 additions & 7 deletions packages/compile/src/generate/gen-code-c.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ void storeOutputData() {
outputVar(_w);
}

void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t subIndex2) {
void storeOutput(size_t varIndex, size_t* subIndices) {
switch (varIndex) {
case 1:
outputVar(_final_time);
Expand All @@ -330,10 +330,10 @@ void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t sub
outputVar(_input);
break;
case 10:
outputVar(_a[subIndex0]);
outputVar(_a[subIndices[0]]);
break;
case 11:
outputVar(_b[subIndex0][subIndex1]);
outputVar(_b[subIndices[0]][subIndices[1]]);
break;
case 12:
outputVar(_c);
Expand All @@ -345,7 +345,7 @@ void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t sub
outputVar(_w);
break;
case 15:
outputVar(_d[subIndex0]);
outputVar(_d[subIndices[0]]);
break;
case 16:
outputVar(_y);
Expand Down Expand Up @@ -446,7 +446,7 @@ void setLookup(size_t varIndex, size_t* subIndices, double* points, size_t numPo
outputVarNames: ['y']
})
expect(code).toMatch(`\
void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t subIndex2) {
void storeOutput(size_t varIndex, size_t* subIndices) {
fprintf(stderr, "The storeOutput function was not enabled for the generated model. Set the customOutputs property in the spec/config file to allow for capturing arbitrary variables at runtime.\\n");
}`)
})
Expand All @@ -469,10 +469,10 @@ void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t sub
customOutputs: ['u[A1]', 'x']
})
expect(code).toMatch(`\
void storeOutput(size_t varIndex, size_t subIndex0, size_t subIndex1, size_t subIndex2) {
void storeOutput(size_t varIndex, size_t* subIndices) {
switch (varIndex) {
case 5:
outputVar(_u[subIndex0]);
outputVar(_u[subIndices[0]]);
break;
case 6:
outputVar(_x);
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/_shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface VarSpec {
/** The variable index as used in the generated C/JS code. */
varIndex: number
/** The subscript index values as used in the generated C/JS code. */
subscriptIndices?: number[]
subscriptIndices?: number[] | Int32Array
}

/**
Expand Down
123 changes: 123 additions & 0 deletions packages/runtime/src/_shared/var-indices.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) 2024 Climate Interactive / New Venture Fund

import { describe, expect, it } from 'vitest'

import { createLookupDef, type LookupDef } from './lookup-def'
import type { VarSpec } from './types'
import {
decodeLookups,
encodeLookups,
encodeVarIndices,
getEncodedLookupBufferLengths,
getEncodedVarIndicesLength
} from './var-indices'

const varSpecs: VarSpec[] = [
{ varIndex: 1 },
{ varIndex: 2 },
{ varIndex: 3, subscriptIndices: [1, 2, 3, 4] },
{ varIndex: 4, subscriptIndices: [1, 2] }
]

describe('getEncodedVarIndicesLength', () => {
it('should return the correct length', () => {
expect(getEncodedVarIndicesLength(varSpecs)).toBe(15)
})
})

describe('encodeVarIndices', () => {
it('should encode the correct values', () => {
const array = new Int32Array(20)
encodeVarIndices(varSpecs, array)
expect(array).toEqual(
new Int32Array([
4, // variable count

1, // var0 index
0, // var0 subscript count

2, // var1 index
0, // var1 subscript count

3, // var2 index
4, // var2 subscript count
1, // var2 sub0 index
2, // var2 sub1 index
3, // var2 sub2 index
4, // var2 sub3 index

4, // var3 index
2, // var2 subscript count
1, // var3 sub0 index
2, // var3 sub1 index

// zero padding
0,
0,
0,
0,
0
])
)
})
})

const p = (x: number, y: number) => ({ x, y })
const lookupDefs: LookupDef[] = [
createLookupDef({ varSpec: { varIndex: 1 } }, [p(0, 0), p(1, 1)]),
createLookupDef({ varSpec: { varIndex: 2, subscriptIndices: [1, 2] } }, [p(0, 0), p(1, 1)])
]

describe('getEncodedLookupBufferLengths', () => {
it('should return the correct length', () => {
const { lookupIndicesLength, lookupsLength } = getEncodedLookupBufferLengths(lookupDefs)
expect(lookupIndicesLength).toBe(11)
expect(lookupsLength).toBe(8)
})
})

describe('encodeLookups and decodeLookups', () => {
it('should encode and decode the correct values', () => {
const lookupIndices = new Int32Array(13)
const lookupValues = new Float64Array(10)
encodeLookups(lookupDefs, lookupIndices, lookupValues)

expect(lookupIndices).toEqual(
new Int32Array([
2, // variable count

1, // var0 index
0, // var0 subscript count
0, // var0 data offset
4, // var0 data length

2, // var1 index
2, // var1 subscript count
1, // var1 sub0 index
2, // var1 sub1 index
4, // var1 data offset
4, // var1 data length

// zero padding
0,
0
])
)

expect(lookupValues).toEqual(
new Float64Array([
// var0 data
0, 0, 1, 1,

// var1 data
0, 0, 1, 1,

// zero padding
0, 0
])
)

const decodedLookupDefs = decodeLookups(lookupIndices, lookupValues)
expect(decodedLookupDefs).toEqual(lookupDefs)
})
})
Loading