@milaboratories/pl-model-common 1.33.0 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,7 +35,7 @@ var LinkerMap = class LinkerMap {
35
35
  keyAxesSpec: spec,
36
36
  linkWith: /* @__PURE__ */ new Map()
37
37
  });
38
- for (const [keyLeft] of leftKeyVariants) for (const [keyRight] of rightKeyVariants) result.get(keyLeft)?.linkWith.set(keyRight, linker);
38
+ for (const [keyRight] of rightKeyVariants) for (const [keyLeft] of leftKeyVariants) result.get(keyRight)?.linkWith.set(keyLeft, linker);
39
39
  }
40
40
  return new this(result);
41
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"linker_columns.cjs","names":["parseJson","readAnnotationJson","Annotation","getNormalizedAxesList","getArrayFromAxisTree","getAxesTree","canonicalizeJson","getAxisId"],"sources":["../../../src/drivers/pframe/linker_columns.ts"],"sourcesContent":["import { canonicalizeJson, parseJson, type CanonicalizedJson } from \"../../json\";\nimport {\n Annotation,\n readAnnotationJson,\n type AxisSpec,\n type PColumnIdAndSpec,\n type AxisSpecNormalized,\n type AxisId,\n getAxisId,\n getNormalizedAxesList,\n getArrayFromAxisTree,\n getAxesTree,\n} from \"./spec/spec\";\n\ntype LinkerKey = CanonicalizedJson<AxisId[]>;\nexport type CompositeLinkerMap = Map<\n LinkerKey,\n {\n keyAxesSpec: AxisSpecNormalized[]; // axis specs - source for the key\n linkWith: Map<LinkerKey, PColumnIdAndSpec>; // for every axis (possibly in group with parents - available by linkers another axes and corresponding linkers)\n }\n>;\n\ninterface LinkersData {\n data: CompositeLinkerMap;\n}\nexport class LinkerMap implements LinkersData {\n /** Graph of linkers connected by axes (single or grouped by parents) */\n readonly data: CompositeLinkerMap;\n\n constructor(linkerMap: CompositeLinkerMap) {\n this.data = linkerMap;\n }\n\n get keys() {\n return this.data.keys();\n }\n\n get keyAxesIds() {\n return [...this.data.keys()].map(parseJson);\n }\n\n static fromColumns(columns: PColumnIdAndSpec[]) {\n const result: CompositeLinkerMap = new Map();\n for (const linker of columns.filter(\n (l) => !!readAnnotationJson(l.spec, Annotation.IsLinkerColumn),\n )) {\n const groups = LinkerMap.getAxesGroups(getNormalizedAxesList(linker.spec.axesSpec)); // split input axes into groups by parent links from annotation\n\n if (groups.length !== 2) {\n continue; // not a valid linker column\n }\n const [left, right] = groups;\n\n // In case of group:\n // A - C\n // \\_ B _ D\n // E/\n // put 2 variants as keys:\n // A - C\n // \\_ B _ D\n // and\n // E - B - D\n const leftKeyVariants: [LinkerKey, AxisSpecNormalized[]][] = LinkerMap.getAxesRoots(left).map(\n (axis) => {\n const axes = getArrayFromAxisTree(getAxesTree(axis));\n const key = canonicalizeJson(axes.map(getAxisId));\n return [key, axes];\n },\n );\n const rightKeyVariants: [LinkerKey, AxisSpecNormalized[]][] = LinkerMap.getAxesRoots(\n right,\n ).map((axis) => {\n const axes = getArrayFromAxisTree(getAxesTree(axis));\n const key = canonicalizeJson(axes.map(getAxisId));\n return [key, axes];\n });\n\n for (const [keyLeft, spec] of leftKeyVariants) {\n if (!result.has(keyLeft)) {\n result.set(keyLeft, { keyAxesSpec: spec, linkWith: new Map() });\n }\n }\n for (const [keyRight, spec] of rightKeyVariants) {\n if (!result.has(keyRight)) {\n result.set(keyRight, { keyAxesSpec: spec, linkWith: new Map() });\n }\n }\n for (const [keyLeft] of leftKeyVariants) {\n for (const [keyRight] of rightKeyVariants) {\n result.get(keyLeft)?.linkWith.set(keyRight, linker);\n }\n }\n }\n return new this(result);\n }\n\n /** Get all available nodes of linker graphs if start from sourceAxesKeys */\n searchAvailableAxesKeys(sourceAxesKeys: LinkerKey[]): Set<LinkerKey> {\n const startKeys = new Set(sourceAxesKeys);\n const allAvailableKeys = new Set<LinkerKey>();\n let nextKeys = sourceAxesKeys;\n while (nextKeys.length) {\n const next: LinkerKey[] = [];\n for (const key of nextKeys) {\n const node = this.data.get(key);\n if (!node) continue;\n for (const availableKey of node.linkWith.keys()) {\n if (!allAvailableKeys.has(availableKey) && !startKeys.has(availableKey)) {\n next.push(availableKey);\n allAvailableKeys.add(availableKey);\n }\n }\n }\n nextKeys = next;\n }\n return allAvailableKeys;\n }\n\n /** Get all linker columns that are necessary to reach endKey from startKey */\n searchLinkerPath(startKey: LinkerKey, endKey: LinkerKey): PColumnIdAndSpec[] {\n const previous: Record<LinkerKey, LinkerKey> = {};\n let nextIds = new Set([startKey]);\n const visited = new Set([startKey]);\n while (nextIds.size) {\n const next = new Set<LinkerKey>();\n for (const nextId of nextIds) {\n const node = this.data.get(nextId);\n if (!node) continue;\n for (const availableId of node.linkWith.keys()) {\n previous[availableId] = nextId;\n if (availableId === endKey) {\n const ids: LinkerKey[] = [];\n let current = endKey;\n while (previous[current] !== startKey) {\n ids.push(current);\n current = previous[current];\n }\n ids.push(current);\n // Edge (previous[id] -> id) is stored at the \"from\" node in one-directional map\n return ids.map((id: LinkerKey) => this.data.get(previous[id])!.linkWith.get(id)!);\n } else if (!visited.has(availableId)) {\n next.add(availableId);\n visited.add(availableId);\n }\n }\n }\n nextIds = next;\n }\n return [];\n }\n\n getLinkerColumnsForAxes({\n from: sourceAxes,\n to: targetAxes,\n throwWhenNoLinkExists = true,\n }: {\n from: AxisSpecNormalized[];\n to: AxisSpecNormalized[];\n throwWhenNoLinkExists?: boolean;\n }): PColumnIdAndSpec[] {\n // start keys - all possible keys in linker map using sourceAxes (for example, all axes of block's columns or all axes of columns in data-inputs)\n const startKeys: LinkerKey[] = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n\n return Array.from(\n new Map(\n LinkerMap.getAxesRoots(targetAxes)\n .map(LinkerMap.getLinkerKeyFromAxisSpec) // target keys contain all axes to be linked; if some of target axes has parents they must be in the key\n .flatMap((targetKey) => {\n const linkers = startKeys\n .map((startKey) => this.searchLinkerPath(startKey, targetKey))\n .reduce(\n (shortestPath, path) =>\n (shortestPath.length && shortestPath.length < path.length) || !path.length\n ? shortestPath\n : path,\n [] as PColumnIdAndSpec[],\n )\n .map((linker) => [linker.columnId, linker] as const);\n if (!linkers.length && throwWhenNoLinkExists) {\n throw Error(`Unable to find linker column for ${targetKey}`);\n }\n return linkers;\n }),\n ).values(),\n );\n }\n\n /** Get list of axisSpecs from keys of linker columns map */\n getAxesListFromKeysList(keys: LinkerKey[]): AxisSpecNormalized[] {\n return Array.from(\n new Map(\n keys\n .flatMap((key) => this.data.get(key)?.keyAxesSpec ?? [])\n .map((axis) => [canonicalizeJson(getAxisId(axis)), axis]),\n ).values(),\n );\n }\n\n /** Get axes of target axes that are impossible to be linked to source axes with current linker map */\n getNonLinkableAxes(\n sourceAxes: AxisSpecNormalized[],\n targetAxes: AxisSpecNormalized[],\n ): AxisSpecNormalized[] {\n const startKeys = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n // target keys contain all axes to be linked; if some of target axes has parents they must be in the key\n const targetKeys = targetAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n\n const axes = Array.from(\n new Map(\n targetAxes\n .filter((_targetAxis, idx) => {\n const targetKey = targetKeys[idx];\n return !startKeys.some((startKey) => this.searchLinkerPath(startKey, targetKey).length);\n })\n .flatMap((axis) =>\n getArrayFromAxisTree(getAxesTree(axis)).map((axis) => [\n canonicalizeJson(getAxisId(axis)),\n axis,\n ]),\n ),\n ).values(),\n );\n return axes;\n }\n\n /** Get all axes that can be connected to sourceAxes by linkers */\n getReachableByLinkersAxesFromAxesNormalized(\n sourceAxes: AxisSpecNormalized[],\n matchAxisIdFn?: (axisIdOfLinker: AxisId, axisIdOfSource: AxisId) => boolean,\n ): AxisSpecNormalized[] {\n let startKeys: CanonicalizedJson<AxisId[]>[] = [];\n\n if (matchAxisIdFn) {\n const sourceAxisIdsGrouped: AxisId[][] = sourceAxes.map((axis) =>\n getArrayFromAxisTree(getAxesTree(axis)).map(getAxisId),\n );\n for (const sourceAxisIdsGroup of sourceAxisIdsGrouped) {\n const matched = this.keyAxesIds.find((keyIds: AxisId[]) =>\n keyIds.every((linkerKeyAxisId) =>\n sourceAxisIdsGroup.find((sourceAxisId) => matchAxisIdFn(linkerKeyAxisId, sourceAxisId)),\n ),\n );\n if (matched) {\n startKeys.push(canonicalizeJson(matched));\n }\n }\n } else {\n startKeys = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n }\n\n const availableKeys = this.searchAvailableAxesKeys(startKeys);\n return this.getAxesListFromKeysList([...availableKeys]);\n }\n\n getReachableByLinkersAxesFromAxes(\n sourceAxes: AxisSpec[],\n matchAxisIdFn?: (axisIdOfLinker: AxisId, axisIdOfSource: AxisId) => boolean,\n ): AxisSpecNormalized[] {\n return this.getReachableByLinkersAxesFromAxesNormalized(\n getNormalizedAxesList(sourceAxes),\n matchAxisIdFn,\n );\n }\n\n static getLinkerKeyFromAxisSpec(axis: AxisSpecNormalized): LinkerKey {\n return canonicalizeJson(getArrayFromAxisTree(getAxesTree(axis)).map(getAxisId));\n }\n\n /** Split array of axes into several arrays by parents: axes of one group are parents for each other.\n There are no order inside every group. */\n static getAxesGroups(axesSpec: AxisSpecNormalized[]): AxisSpecNormalized[][] {\n switch (axesSpec.length) {\n case 0:\n return [];\n case 1:\n return [[axesSpec[0]]];\n default:\n break;\n }\n\n const axisKeys = axesSpec.map((spec) => canonicalizeJson(getAxisId(spec)));\n const axisParentsIdxs = axesSpec.map(\n (spec) =>\n new Set(\n spec.parentAxesSpec\n .map((spec) => canonicalizeJson(getAxisId(spec)))\n .map((el) => {\n const idx = axisKeys.indexOf(el);\n if (idx === -1) {\n throw new Error(\n `malformed axesSpec: ${JSON.stringify(axesSpec)}, unable to locate parent ${el}`,\n );\n }\n return idx;\n }),\n ),\n );\n\n const allIdxs = [...axesSpec.keys()];\n const groups: number[][] = []; // groups of axis indexes\n\n const usedIdxs = new Set<number>();\n let nextFreeEl = allIdxs.find((idx) => !usedIdxs.has(idx));\n while (nextFreeEl !== undefined) {\n const currentGroup = [nextFreeEl];\n usedIdxs.add(nextFreeEl);\n\n let nextElsOfCurrentGroup = [nextFreeEl];\n while (nextElsOfCurrentGroup.length) {\n const next = new Set<number>();\n for (const groupIdx of nextElsOfCurrentGroup) {\n const groupElementParents = axisParentsIdxs[groupIdx];\n allIdxs.forEach((idx) => {\n if (idx === groupIdx || usedIdxs.has(idx)) {\n return;\n }\n const parents = axisParentsIdxs[idx];\n if (parents.has(groupIdx) || groupElementParents.has(idx)) {\n currentGroup.push(idx);\n next.add(idx);\n usedIdxs.add(idx);\n }\n });\n }\n nextElsOfCurrentGroup = [...next];\n }\n\n groups.push([...currentGroup]);\n nextFreeEl = allIdxs.find((idx) => !usedIdxs.has(idx));\n }\n\n return groups.map((group) => group.map((idx) => axesSpec[idx]));\n }\n\n /** Get all axes that are not parents of any other axis */\n static getAxesRoots(axes: AxisSpecNormalized[]): AxisSpecNormalized[] {\n const parentsSet = new Set(\n axes.flatMap((axis) => axis.parentAxesSpec).map((spec) => canonicalizeJson(getAxisId(spec))),\n );\n return axes.filter((axis) => !parentsSet.has(canonicalizeJson(getAxisId(axis))));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"linker_columns.cjs","names":["parseJson","readAnnotationJson","Annotation","getNormalizedAxesList","getArrayFromAxisTree","getAxesTree","canonicalizeJson","getAxisId"],"sources":["../../../src/drivers/pframe/linker_columns.ts"],"sourcesContent":["import { canonicalizeJson, parseJson, type CanonicalizedJson } from \"../../json\";\nimport {\n Annotation,\n readAnnotationJson,\n type AxisSpec,\n type PColumnIdAndSpec,\n type AxisSpecNormalized,\n type AxisId,\n getAxisId,\n getNormalizedAxesList,\n getArrayFromAxisTree,\n getAxesTree,\n} from \"./spec/spec\";\n\ntype LinkerKey = CanonicalizedJson<AxisId[]>;\nexport type CompositeLinkerMap = Map<\n LinkerKey,\n {\n keyAxesSpec: AxisSpecNormalized[]; // axis specs - source for the key\n linkWith: Map<LinkerKey, PColumnIdAndSpec>; // for every axis (possibly in group with parents - available by linkers another axes and corresponding linkers)\n }\n>;\n\ninterface LinkersData {\n data: CompositeLinkerMap;\n}\nexport class LinkerMap implements LinkersData {\n /** Graph of linkers connected by axes (single or grouped by parents) */\n readonly data: CompositeLinkerMap;\n\n constructor(linkerMap: CompositeLinkerMap) {\n this.data = linkerMap;\n }\n\n get keys() {\n return this.data.keys();\n }\n\n get keyAxesIds() {\n return [...this.data.keys()].map(parseJson);\n }\n\n static fromColumns(columns: PColumnIdAndSpec[]) {\n const result: CompositeLinkerMap = new Map();\n for (const linker of columns.filter(\n (l) => !!readAnnotationJson(l.spec, Annotation.IsLinkerColumn),\n )) {\n const groups = LinkerMap.getAxesGroups(getNormalizedAxesList(linker.spec.axesSpec)); // split input axes into groups by parent links from annotation\n\n if (groups.length !== 2) {\n continue; // not a valid linker column\n }\n const [left, right] = groups;\n\n // In case of group:\n // A - C\n // \\_ B _ D\n // E/\n // put 2 variants as keys:\n // A - C\n // \\_ B _ D\n // and\n // E - B - D\n const leftKeyVariants: [LinkerKey, AxisSpecNormalized[]][] = LinkerMap.getAxesRoots(left).map(\n (axis) => {\n const axes = getArrayFromAxisTree(getAxesTree(axis));\n const key = canonicalizeJson(axes.map(getAxisId));\n return [key, axes];\n },\n );\n const rightKeyVariants: [LinkerKey, AxisSpecNormalized[]][] = LinkerMap.getAxesRoots(\n right,\n ).map((axis) => {\n const axes = getArrayFromAxisTree(getAxesTree(axis));\n const key = canonicalizeJson(axes.map(getAxisId));\n return [key, axes];\n });\n\n for (const [keyLeft, spec] of leftKeyVariants) {\n if (!result.has(keyLeft)) {\n result.set(keyLeft, { keyAxesSpec: spec, linkWith: new Map() });\n }\n }\n for (const [keyRight, spec] of rightKeyVariants) {\n if (!result.has(keyRight)) {\n result.set(keyRight, { keyAxesSpec: spec, linkWith: new Map() });\n }\n }\n for (const [keyRight] of rightKeyVariants) {\n for (const [keyLeft] of leftKeyVariants) {\n result.get(keyRight)?.linkWith.set(keyLeft, linker);\n }\n }\n }\n return new this(result);\n }\n\n /** Get all available nodes of linker graphs if start from sourceAxesKeys */\n searchAvailableAxesKeys(sourceAxesKeys: LinkerKey[]): Set<LinkerKey> {\n const startKeys = new Set(sourceAxesKeys);\n const allAvailableKeys = new Set<LinkerKey>();\n let nextKeys = sourceAxesKeys;\n while (nextKeys.length) {\n const next: LinkerKey[] = [];\n for (const key of nextKeys) {\n const node = this.data.get(key);\n if (!node) continue;\n for (const availableKey of node.linkWith.keys()) {\n if (!allAvailableKeys.has(availableKey) && !startKeys.has(availableKey)) {\n next.push(availableKey);\n allAvailableKeys.add(availableKey);\n }\n }\n }\n nextKeys = next;\n }\n return allAvailableKeys;\n }\n\n /** Get all linker columns that are necessary to reach endKey from startKey */\n searchLinkerPath(startKey: LinkerKey, endKey: LinkerKey): PColumnIdAndSpec[] {\n const previous: Record<LinkerKey, LinkerKey> = {};\n let nextIds = new Set([startKey]);\n const visited = new Set([startKey]);\n while (nextIds.size) {\n const next = new Set<LinkerKey>();\n for (const nextId of nextIds) {\n const node = this.data.get(nextId);\n if (!node) continue;\n for (const availableId of node.linkWith.keys()) {\n previous[availableId] = nextId;\n if (availableId === endKey) {\n const ids: LinkerKey[] = [];\n let current = endKey;\n while (previous[current] !== startKey) {\n ids.push(current);\n current = previous[current];\n }\n ids.push(current);\n // Edge (previous[id] -> id) is stored at the \"from\" node in one-directional map\n return ids.map((id: LinkerKey) => this.data.get(previous[id])!.linkWith.get(id)!);\n } else if (!visited.has(availableId)) {\n next.add(availableId);\n visited.add(availableId);\n }\n }\n }\n nextIds = next;\n }\n return [];\n }\n\n getLinkerColumnsForAxes({\n from: sourceAxes,\n to: targetAxes,\n throwWhenNoLinkExists = true,\n }: {\n from: AxisSpecNormalized[];\n to: AxisSpecNormalized[];\n throwWhenNoLinkExists?: boolean;\n }): PColumnIdAndSpec[] {\n // start keys - all possible keys in linker map using sourceAxes (for example, all axes of block's columns or all axes of columns in data-inputs)\n const startKeys: LinkerKey[] = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n\n return Array.from(\n new Map(\n LinkerMap.getAxesRoots(targetAxes)\n .map(LinkerMap.getLinkerKeyFromAxisSpec) // target keys contain all axes to be linked; if some of target axes has parents they must be in the key\n .flatMap((targetKey) => {\n const linkers = startKeys\n .map((startKey) => this.searchLinkerPath(startKey, targetKey))\n .reduce(\n (shortestPath, path) =>\n (shortestPath.length && shortestPath.length < path.length) || !path.length\n ? shortestPath\n : path,\n [] as PColumnIdAndSpec[],\n )\n .map((linker) => [linker.columnId, linker] as const);\n if (!linkers.length && throwWhenNoLinkExists) {\n throw Error(`Unable to find linker column for ${targetKey}`);\n }\n return linkers;\n }),\n ).values(),\n );\n }\n\n /** Get list of axisSpecs from keys of linker columns map */\n getAxesListFromKeysList(keys: LinkerKey[]): AxisSpecNormalized[] {\n return Array.from(\n new Map(\n keys\n .flatMap((key) => this.data.get(key)?.keyAxesSpec ?? [])\n .map((axis) => [canonicalizeJson(getAxisId(axis)), axis]),\n ).values(),\n );\n }\n\n /** Get axes of target axes that are impossible to be linked to source axes with current linker map */\n getNonLinkableAxes(\n sourceAxes: AxisSpecNormalized[],\n targetAxes: AxisSpecNormalized[],\n ): AxisSpecNormalized[] {\n const startKeys = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n // target keys contain all axes to be linked; if some of target axes has parents they must be in the key\n const targetKeys = targetAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n\n const axes = Array.from(\n new Map(\n targetAxes\n .filter((_targetAxis, idx) => {\n const targetKey = targetKeys[idx];\n return !startKeys.some((startKey) => this.searchLinkerPath(startKey, targetKey).length);\n })\n .flatMap((axis) =>\n getArrayFromAxisTree(getAxesTree(axis)).map((axis) => [\n canonicalizeJson(getAxisId(axis)),\n axis,\n ]),\n ),\n ).values(),\n );\n return axes;\n }\n\n /** Get all axes that can be connected to sourceAxes by linkers */\n getReachableByLinkersAxesFromAxesNormalized(\n sourceAxes: AxisSpecNormalized[],\n matchAxisIdFn?: (axisIdOfLinker: AxisId, axisIdOfSource: AxisId) => boolean,\n ): AxisSpecNormalized[] {\n let startKeys: CanonicalizedJson<AxisId[]>[] = [];\n\n if (matchAxisIdFn) {\n const sourceAxisIdsGrouped: AxisId[][] = sourceAxes.map((axis) =>\n getArrayFromAxisTree(getAxesTree(axis)).map(getAxisId),\n );\n for (const sourceAxisIdsGroup of sourceAxisIdsGrouped) {\n const matched = this.keyAxesIds.find((keyIds: AxisId[]) =>\n keyIds.every((linkerKeyAxisId) =>\n sourceAxisIdsGroup.find((sourceAxisId) => matchAxisIdFn(linkerKeyAxisId, sourceAxisId)),\n ),\n );\n if (matched) {\n startKeys.push(canonicalizeJson(matched));\n }\n }\n } else {\n startKeys = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n }\n\n const availableKeys = this.searchAvailableAxesKeys(startKeys);\n return this.getAxesListFromKeysList([...availableKeys]);\n }\n\n getReachableByLinkersAxesFromAxes(\n sourceAxes: AxisSpec[],\n matchAxisIdFn?: (axisIdOfLinker: AxisId, axisIdOfSource: AxisId) => boolean,\n ): AxisSpecNormalized[] {\n return this.getReachableByLinkersAxesFromAxesNormalized(\n getNormalizedAxesList(sourceAxes),\n matchAxisIdFn,\n );\n }\n\n static getLinkerKeyFromAxisSpec(axis: AxisSpecNormalized): LinkerKey {\n return canonicalizeJson(getArrayFromAxisTree(getAxesTree(axis)).map(getAxisId));\n }\n\n /** Split array of axes into several arrays by parents: axes of one group are parents for each other.\n There are no order inside every group. */\n static getAxesGroups(axesSpec: AxisSpecNormalized[]): AxisSpecNormalized[][] {\n switch (axesSpec.length) {\n case 0:\n return [];\n case 1:\n return [[axesSpec[0]]];\n default:\n break;\n }\n\n const axisKeys = axesSpec.map((spec) => canonicalizeJson(getAxisId(spec)));\n const axisParentsIdxs = axesSpec.map(\n (spec) =>\n new Set(\n spec.parentAxesSpec\n .map((spec) => canonicalizeJson(getAxisId(spec)))\n .map((el) => {\n const idx = axisKeys.indexOf(el);\n if (idx === -1) {\n throw new Error(\n `malformed axesSpec: ${JSON.stringify(axesSpec)}, unable to locate parent ${el}`,\n );\n }\n return idx;\n }),\n ),\n );\n\n const allIdxs = [...axesSpec.keys()];\n const groups: number[][] = []; // groups of axis indexes\n\n const usedIdxs = new Set<number>();\n let nextFreeEl = allIdxs.find((idx) => !usedIdxs.has(idx));\n while (nextFreeEl !== undefined) {\n const currentGroup = [nextFreeEl];\n usedIdxs.add(nextFreeEl);\n\n let nextElsOfCurrentGroup = [nextFreeEl];\n while (nextElsOfCurrentGroup.length) {\n const next = new Set<number>();\n for (const groupIdx of nextElsOfCurrentGroup) {\n const groupElementParents = axisParentsIdxs[groupIdx];\n allIdxs.forEach((idx) => {\n if (idx === groupIdx || usedIdxs.has(idx)) {\n return;\n }\n const parents = axisParentsIdxs[idx];\n if (parents.has(groupIdx) || groupElementParents.has(idx)) {\n currentGroup.push(idx);\n next.add(idx);\n usedIdxs.add(idx);\n }\n });\n }\n nextElsOfCurrentGroup = [...next];\n }\n\n groups.push([...currentGroup]);\n nextFreeEl = allIdxs.find((idx) => !usedIdxs.has(idx));\n }\n\n return groups.map((group) => group.map((idx) => axesSpec[idx]));\n }\n\n /** Get all axes that are not parents of any other axis */\n static getAxesRoots(axes: AxisSpecNormalized[]): AxisSpecNormalized[] {\n const parentsSet = new Set(\n axes.flatMap((axis) => axis.parentAxesSpec).map((spec) => canonicalizeJson(getAxisId(spec))),\n );\n return axes.filter((axis) => !parentsSet.has(canonicalizeJson(getAxisId(axis))));\n }\n}\n"],"mappings":"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"}
@@ -35,7 +35,7 @@ var LinkerMap = class LinkerMap {
35
35
  keyAxesSpec: spec,
36
36
  linkWith: /* @__PURE__ */ new Map()
37
37
  });
38
- for (const [keyLeft] of leftKeyVariants) for (const [keyRight] of rightKeyVariants) result.get(keyLeft)?.linkWith.set(keyRight, linker);
38
+ for (const [keyRight] of rightKeyVariants) for (const [keyLeft] of leftKeyVariants) result.get(keyRight)?.linkWith.set(keyLeft, linker);
39
39
  }
40
40
  return new this(result);
41
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"linker_columns.js","names":[],"sources":["../../../src/drivers/pframe/linker_columns.ts"],"sourcesContent":["import { canonicalizeJson, parseJson, type CanonicalizedJson } from \"../../json\";\nimport {\n Annotation,\n readAnnotationJson,\n type AxisSpec,\n type PColumnIdAndSpec,\n type AxisSpecNormalized,\n type AxisId,\n getAxisId,\n getNormalizedAxesList,\n getArrayFromAxisTree,\n getAxesTree,\n} from \"./spec/spec\";\n\ntype LinkerKey = CanonicalizedJson<AxisId[]>;\nexport type CompositeLinkerMap = Map<\n LinkerKey,\n {\n keyAxesSpec: AxisSpecNormalized[]; // axis specs - source for the key\n linkWith: Map<LinkerKey, PColumnIdAndSpec>; // for every axis (possibly in group with parents - available by linkers another axes and corresponding linkers)\n }\n>;\n\ninterface LinkersData {\n data: CompositeLinkerMap;\n}\nexport class LinkerMap implements LinkersData {\n /** Graph of linkers connected by axes (single or grouped by parents) */\n readonly data: CompositeLinkerMap;\n\n constructor(linkerMap: CompositeLinkerMap) {\n this.data = linkerMap;\n }\n\n get keys() {\n return this.data.keys();\n }\n\n get keyAxesIds() {\n return [...this.data.keys()].map(parseJson);\n }\n\n static fromColumns(columns: PColumnIdAndSpec[]) {\n const result: CompositeLinkerMap = new Map();\n for (const linker of columns.filter(\n (l) => !!readAnnotationJson(l.spec, Annotation.IsLinkerColumn),\n )) {\n const groups = LinkerMap.getAxesGroups(getNormalizedAxesList(linker.spec.axesSpec)); // split input axes into groups by parent links from annotation\n\n if (groups.length !== 2) {\n continue; // not a valid linker column\n }\n const [left, right] = groups;\n\n // In case of group:\n // A - C\n // \\_ B _ D\n // E/\n // put 2 variants as keys:\n // A - C\n // \\_ B _ D\n // and\n // E - B - D\n const leftKeyVariants: [LinkerKey, AxisSpecNormalized[]][] = LinkerMap.getAxesRoots(left).map(\n (axis) => {\n const axes = getArrayFromAxisTree(getAxesTree(axis));\n const key = canonicalizeJson(axes.map(getAxisId));\n return [key, axes];\n },\n );\n const rightKeyVariants: [LinkerKey, AxisSpecNormalized[]][] = LinkerMap.getAxesRoots(\n right,\n ).map((axis) => {\n const axes = getArrayFromAxisTree(getAxesTree(axis));\n const key = canonicalizeJson(axes.map(getAxisId));\n return [key, axes];\n });\n\n for (const [keyLeft, spec] of leftKeyVariants) {\n if (!result.has(keyLeft)) {\n result.set(keyLeft, { keyAxesSpec: spec, linkWith: new Map() });\n }\n }\n for (const [keyRight, spec] of rightKeyVariants) {\n if (!result.has(keyRight)) {\n result.set(keyRight, { keyAxesSpec: spec, linkWith: new Map() });\n }\n }\n for (const [keyLeft] of leftKeyVariants) {\n for (const [keyRight] of rightKeyVariants) {\n result.get(keyLeft)?.linkWith.set(keyRight, linker);\n }\n }\n }\n return new this(result);\n }\n\n /** Get all available nodes of linker graphs if start from sourceAxesKeys */\n searchAvailableAxesKeys(sourceAxesKeys: LinkerKey[]): Set<LinkerKey> {\n const startKeys = new Set(sourceAxesKeys);\n const allAvailableKeys = new Set<LinkerKey>();\n let nextKeys = sourceAxesKeys;\n while (nextKeys.length) {\n const next: LinkerKey[] = [];\n for (const key of nextKeys) {\n const node = this.data.get(key);\n if (!node) continue;\n for (const availableKey of node.linkWith.keys()) {\n if (!allAvailableKeys.has(availableKey) && !startKeys.has(availableKey)) {\n next.push(availableKey);\n allAvailableKeys.add(availableKey);\n }\n }\n }\n nextKeys = next;\n }\n return allAvailableKeys;\n }\n\n /** Get all linker columns that are necessary to reach endKey from startKey */\n searchLinkerPath(startKey: LinkerKey, endKey: LinkerKey): PColumnIdAndSpec[] {\n const previous: Record<LinkerKey, LinkerKey> = {};\n let nextIds = new Set([startKey]);\n const visited = new Set([startKey]);\n while (nextIds.size) {\n const next = new Set<LinkerKey>();\n for (const nextId of nextIds) {\n const node = this.data.get(nextId);\n if (!node) continue;\n for (const availableId of node.linkWith.keys()) {\n previous[availableId] = nextId;\n if (availableId === endKey) {\n const ids: LinkerKey[] = [];\n let current = endKey;\n while (previous[current] !== startKey) {\n ids.push(current);\n current = previous[current];\n }\n ids.push(current);\n // Edge (previous[id] -> id) is stored at the \"from\" node in one-directional map\n return ids.map((id: LinkerKey) => this.data.get(previous[id])!.linkWith.get(id)!);\n } else if (!visited.has(availableId)) {\n next.add(availableId);\n visited.add(availableId);\n }\n }\n }\n nextIds = next;\n }\n return [];\n }\n\n getLinkerColumnsForAxes({\n from: sourceAxes,\n to: targetAxes,\n throwWhenNoLinkExists = true,\n }: {\n from: AxisSpecNormalized[];\n to: AxisSpecNormalized[];\n throwWhenNoLinkExists?: boolean;\n }): PColumnIdAndSpec[] {\n // start keys - all possible keys in linker map using sourceAxes (for example, all axes of block's columns or all axes of columns in data-inputs)\n const startKeys: LinkerKey[] = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n\n return Array.from(\n new Map(\n LinkerMap.getAxesRoots(targetAxes)\n .map(LinkerMap.getLinkerKeyFromAxisSpec) // target keys contain all axes to be linked; if some of target axes has parents they must be in the key\n .flatMap((targetKey) => {\n const linkers = startKeys\n .map((startKey) => this.searchLinkerPath(startKey, targetKey))\n .reduce(\n (shortestPath, path) =>\n (shortestPath.length && shortestPath.length < path.length) || !path.length\n ? shortestPath\n : path,\n [] as PColumnIdAndSpec[],\n )\n .map((linker) => [linker.columnId, linker] as const);\n if (!linkers.length && throwWhenNoLinkExists) {\n throw Error(`Unable to find linker column for ${targetKey}`);\n }\n return linkers;\n }),\n ).values(),\n );\n }\n\n /** Get list of axisSpecs from keys of linker columns map */\n getAxesListFromKeysList(keys: LinkerKey[]): AxisSpecNormalized[] {\n return Array.from(\n new Map(\n keys\n .flatMap((key) => this.data.get(key)?.keyAxesSpec ?? [])\n .map((axis) => [canonicalizeJson(getAxisId(axis)), axis]),\n ).values(),\n );\n }\n\n /** Get axes of target axes that are impossible to be linked to source axes with current linker map */\n getNonLinkableAxes(\n sourceAxes: AxisSpecNormalized[],\n targetAxes: AxisSpecNormalized[],\n ): AxisSpecNormalized[] {\n const startKeys = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n // target keys contain all axes to be linked; if some of target axes has parents they must be in the key\n const targetKeys = targetAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n\n const axes = Array.from(\n new Map(\n targetAxes\n .filter((_targetAxis, idx) => {\n const targetKey = targetKeys[idx];\n return !startKeys.some((startKey) => this.searchLinkerPath(startKey, targetKey).length);\n })\n .flatMap((axis) =>\n getArrayFromAxisTree(getAxesTree(axis)).map((axis) => [\n canonicalizeJson(getAxisId(axis)),\n axis,\n ]),\n ),\n ).values(),\n );\n return axes;\n }\n\n /** Get all axes that can be connected to sourceAxes by linkers */\n getReachableByLinkersAxesFromAxesNormalized(\n sourceAxes: AxisSpecNormalized[],\n matchAxisIdFn?: (axisIdOfLinker: AxisId, axisIdOfSource: AxisId) => boolean,\n ): AxisSpecNormalized[] {\n let startKeys: CanonicalizedJson<AxisId[]>[] = [];\n\n if (matchAxisIdFn) {\n const sourceAxisIdsGrouped: AxisId[][] = sourceAxes.map((axis) =>\n getArrayFromAxisTree(getAxesTree(axis)).map(getAxisId),\n );\n for (const sourceAxisIdsGroup of sourceAxisIdsGrouped) {\n const matched = this.keyAxesIds.find((keyIds: AxisId[]) =>\n keyIds.every((linkerKeyAxisId) =>\n sourceAxisIdsGroup.find((sourceAxisId) => matchAxisIdFn(linkerKeyAxisId, sourceAxisId)),\n ),\n );\n if (matched) {\n startKeys.push(canonicalizeJson(matched));\n }\n }\n } else {\n startKeys = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n }\n\n const availableKeys = this.searchAvailableAxesKeys(startKeys);\n return this.getAxesListFromKeysList([...availableKeys]);\n }\n\n getReachableByLinkersAxesFromAxes(\n sourceAxes: AxisSpec[],\n matchAxisIdFn?: (axisIdOfLinker: AxisId, axisIdOfSource: AxisId) => boolean,\n ): AxisSpecNormalized[] {\n return this.getReachableByLinkersAxesFromAxesNormalized(\n getNormalizedAxesList(sourceAxes),\n matchAxisIdFn,\n );\n }\n\n static getLinkerKeyFromAxisSpec(axis: AxisSpecNormalized): LinkerKey {\n return canonicalizeJson(getArrayFromAxisTree(getAxesTree(axis)).map(getAxisId));\n }\n\n /** Split array of axes into several arrays by parents: axes of one group are parents for each other.\n There are no order inside every group. */\n static getAxesGroups(axesSpec: AxisSpecNormalized[]): AxisSpecNormalized[][] {\n switch (axesSpec.length) {\n case 0:\n return [];\n case 1:\n return [[axesSpec[0]]];\n default:\n break;\n }\n\n const axisKeys = axesSpec.map((spec) => canonicalizeJson(getAxisId(spec)));\n const axisParentsIdxs = axesSpec.map(\n (spec) =>\n new Set(\n spec.parentAxesSpec\n .map((spec) => canonicalizeJson(getAxisId(spec)))\n .map((el) => {\n const idx = axisKeys.indexOf(el);\n if (idx === -1) {\n throw new Error(\n `malformed axesSpec: ${JSON.stringify(axesSpec)}, unable to locate parent ${el}`,\n );\n }\n return idx;\n }),\n ),\n );\n\n const allIdxs = [...axesSpec.keys()];\n const groups: number[][] = []; // groups of axis indexes\n\n const usedIdxs = new Set<number>();\n let nextFreeEl = allIdxs.find((idx) => !usedIdxs.has(idx));\n while (nextFreeEl !== undefined) {\n const currentGroup = [nextFreeEl];\n usedIdxs.add(nextFreeEl);\n\n let nextElsOfCurrentGroup = [nextFreeEl];\n while (nextElsOfCurrentGroup.length) {\n const next = new Set<number>();\n for (const groupIdx of nextElsOfCurrentGroup) {\n const groupElementParents = axisParentsIdxs[groupIdx];\n allIdxs.forEach((idx) => {\n if (idx === groupIdx || usedIdxs.has(idx)) {\n return;\n }\n const parents = axisParentsIdxs[idx];\n if (parents.has(groupIdx) || groupElementParents.has(idx)) {\n currentGroup.push(idx);\n next.add(idx);\n usedIdxs.add(idx);\n }\n });\n }\n nextElsOfCurrentGroup = [...next];\n }\n\n groups.push([...currentGroup]);\n nextFreeEl = allIdxs.find((idx) => !usedIdxs.has(idx));\n }\n\n return groups.map((group) => group.map((idx) => axesSpec[idx]));\n }\n\n /** Get all axes that are not parents of any other axis */\n static getAxesRoots(axes: AxisSpecNormalized[]): AxisSpecNormalized[] {\n const parentsSet = new Set(\n axes.flatMap((axis) => axis.parentAxesSpec).map((spec) => canonicalizeJson(getAxisId(spec))),\n );\n return axes.filter((axis) => !parentsSet.has(canonicalizeJson(getAxisId(axis))));\n }\n}\n"],"mappings":";;;AA0BA,IAAa,YAAb,MAAa,UAAiC;;CAE5C;CAEA,YAAY,WAA+B;AACzC,OAAK,OAAO;;CAGd,IAAI,OAAO;AACT,SAAO,KAAK,KAAK,MAAM;;CAGzB,IAAI,aAAa;AACf,SAAO,CAAC,GAAG,KAAK,KAAK,MAAM,CAAC,CAAC,IAAI,UAAU;;CAG7C,OAAO,YAAY,SAA6B;EAC9C,MAAM,yBAA6B,IAAI,KAAK;AAC5C,OAAK,MAAM,UAAU,QAAQ,QAC1B,MAAM,CAAC,CAAC,mBAAmB,EAAE,MAAM,WAAW,eAAe,CAC/D,EAAE;GACD,MAAM,SAAS,UAAU,cAAc,sBAAsB,OAAO,KAAK,SAAS,CAAC;AAEnF,OAAI,OAAO,WAAW,EACpB;GAEF,MAAM,CAAC,MAAM,SAAS;GAWtB,MAAM,kBAAuD,UAAU,aAAa,KAAK,CAAC,KACvF,SAAS;IACR,MAAM,OAAO,qBAAqB,YAAY,KAAK,CAAC;AAEpD,WAAO,CADK,iBAAiB,KAAK,IAAI,UAAU,CAAC,EACpC,KAAK;KAErB;GACD,MAAM,mBAAwD,UAAU,aACtE,MACD,CAAC,KAAK,SAAS;IACd,MAAM,OAAO,qBAAqB,YAAY,KAAK,CAAC;AAEpD,WAAO,CADK,iBAAiB,KAAK,IAAI,UAAU,CAAC,EACpC,KAAK;KAClB;AAEF,QAAK,MAAM,CAAC,SAAS,SAAS,gBAC5B,KAAI,CAAC,OAAO,IAAI,QAAQ,CACtB,QAAO,IAAI,SAAS;IAAE,aAAa;IAAM,0BAAU,IAAI,KAAK;IAAE,CAAC;AAGnE,QAAK,MAAM,CAAC,UAAU,SAAS,iBAC7B,KAAI,CAAC,OAAO,IAAI,SAAS,CACvB,QAAO,IAAI,UAAU;IAAE,aAAa;IAAM,0BAAU,IAAI,KAAK;IAAE,CAAC;AAGpE,QAAK,MAAM,CAAC,YAAY,gBACtB,MAAK,MAAM,CAAC,aAAa,iBACvB,QAAO,IAAI,QAAQ,EAAE,SAAS,IAAI,UAAU,OAAO;;AAIzD,SAAO,IAAI,KAAK,OAAO;;;CAIzB,wBAAwB,gBAA6C;EACnE,MAAM,YAAY,IAAI,IAAI,eAAe;EACzC,MAAM,mCAAmB,IAAI,KAAgB;EAC7C,IAAI,WAAW;AACf,SAAO,SAAS,QAAQ;GACtB,MAAM,OAAoB,EAAE;AAC5B,QAAK,MAAM,OAAO,UAAU;IAC1B,MAAM,OAAO,KAAK,KAAK,IAAI,IAAI;AAC/B,QAAI,CAAC,KAAM;AACX,SAAK,MAAM,gBAAgB,KAAK,SAAS,MAAM,CAC7C,KAAI,CAAC,iBAAiB,IAAI,aAAa,IAAI,CAAC,UAAU,IAAI,aAAa,EAAE;AACvE,UAAK,KAAK,aAAa;AACvB,sBAAiB,IAAI,aAAa;;;AAIxC,cAAW;;AAEb,SAAO;;;CAIT,iBAAiB,UAAqB,QAAuC;EAC3E,MAAM,WAAyC,EAAE;EACjD,IAAI,UAAU,IAAI,IAAI,CAAC,SAAS,CAAC;EACjC,MAAM,UAAU,IAAI,IAAI,CAAC,SAAS,CAAC;AACnC,SAAO,QAAQ,MAAM;GACnB,MAAM,uBAAO,IAAI,KAAgB;AACjC,QAAK,MAAM,UAAU,SAAS;IAC5B,MAAM,OAAO,KAAK,KAAK,IAAI,OAAO;AAClC,QAAI,CAAC,KAAM;AACX,SAAK,MAAM,eAAe,KAAK,SAAS,MAAM,EAAE;AAC9C,cAAS,eAAe;AACxB,SAAI,gBAAgB,QAAQ;MAC1B,MAAM,MAAmB,EAAE;MAC3B,IAAI,UAAU;AACd,aAAO,SAAS,aAAa,UAAU;AACrC,WAAI,KAAK,QAAQ;AACjB,iBAAU,SAAS;;AAErB,UAAI,KAAK,QAAQ;AAEjB,aAAO,IAAI,KAAK,OAAkB,KAAK,KAAK,IAAI,SAAS,IAAI,CAAE,SAAS,IAAI,GAAG,CAAE;gBACxE,CAAC,QAAQ,IAAI,YAAY,EAAE;AACpC,WAAK,IAAI,YAAY;AACrB,cAAQ,IAAI,YAAY;;;;AAI9B,aAAU;;AAEZ,SAAO,EAAE;;CAGX,wBAAwB,EACtB,MAAM,YACN,IAAI,YACJ,wBAAwB,QAKH;EAErB,MAAM,YAAyB,WAAW,IAAI,UAAU,yBAAyB;AAEjF,SAAO,MAAM,KACX,IAAI,IACF,UAAU,aAAa,WAAW,CAC/B,IAAI,UAAU,yBAAyB,CACvC,SAAS,cAAc;GACtB,MAAM,UAAU,UACb,KAAK,aAAa,KAAK,iBAAiB,UAAU,UAAU,CAAC,CAC7D,QACE,cAAc,SACZ,aAAa,UAAU,aAAa,SAAS,KAAK,UAAW,CAAC,KAAK,SAChE,eACA,MACN,EAAE,CACH,CACA,KAAK,WAAW,CAAC,OAAO,UAAU,OAAO,CAAU;AACtD,OAAI,CAAC,QAAQ,UAAU,sBACrB,OAAM,MAAM,oCAAoC,YAAY;AAE9D,UAAO;IACP,CACL,CAAC,QAAQ,CACX;;;CAIH,wBAAwB,MAAyC;AAC/D,SAAO,MAAM,KACX,IAAI,IACF,KACG,SAAS,QAAQ,KAAK,KAAK,IAAI,IAAI,EAAE,eAAe,EAAE,CAAC,CACvD,KAAK,SAAS,CAAC,iBAAiB,UAAU,KAAK,CAAC,EAAE,KAAK,CAAC,CAC5D,CAAC,QAAQ,CACX;;;CAIH,mBACE,YACA,YACsB;EACtB,MAAM,YAAY,WAAW,IAAI,UAAU,yBAAyB;EAEpE,MAAM,aAAa,WAAW,IAAI,UAAU,yBAAyB;AAiBrE,SAfa,MAAM,KACjB,IAAI,IACF,WACG,QAAQ,aAAa,QAAQ;GAC5B,MAAM,YAAY,WAAW;AAC7B,UAAO,CAAC,UAAU,MAAM,aAAa,KAAK,iBAAiB,UAAU,UAAU,CAAC,OAAO;IACvF,CACD,SAAS,SACR,qBAAqB,YAAY,KAAK,CAAC,CAAC,KAAK,SAAS,CACpD,iBAAiB,UAAU,KAAK,CAAC,EACjC,KACD,CAAC,CACH,CACJ,CAAC,QAAQ,CACX;;;CAKH,4CACE,YACA,eACsB;EACtB,IAAI,YAA2C,EAAE;AAEjD,MAAI,eAAe;GACjB,MAAM,uBAAmC,WAAW,KAAK,SACvD,qBAAqB,YAAY,KAAK,CAAC,CAAC,IAAI,UAAU,CACvD;AACD,QAAK,MAAM,sBAAsB,sBAAsB;IACrD,MAAM,UAAU,KAAK,WAAW,MAAM,WACpC,OAAO,OAAO,oBACZ,mBAAmB,MAAM,iBAAiB,cAAc,iBAAiB,aAAa,CAAC,CACxF,CACF;AACD,QAAI,QACF,WAAU,KAAK,iBAAiB,QAAQ,CAAC;;QAI7C,aAAY,WAAW,IAAI,UAAU,yBAAyB;EAGhE,MAAM,gBAAgB,KAAK,wBAAwB,UAAU;AAC7D,SAAO,KAAK,wBAAwB,CAAC,GAAG,cAAc,CAAC;;CAGzD,kCACE,YACA,eACsB;AACtB,SAAO,KAAK,4CACV,sBAAsB,WAAW,EACjC,cACD;;CAGH,OAAO,yBAAyB,MAAqC;AACnE,SAAO,iBAAiB,qBAAqB,YAAY,KAAK,CAAC,CAAC,IAAI,UAAU,CAAC;;;;CAKjF,OAAO,cAAc,UAAwD;AAC3E,UAAQ,SAAS,QAAjB;GACE,KAAK,EACH,QAAO,EAAE;GACX,KAAK,EACH,QAAO,CAAC,CAAC,SAAS,GAAG,CAAC;GACxB,QACE;;EAGJ,MAAM,WAAW,SAAS,KAAK,SAAS,iBAAiB,UAAU,KAAK,CAAC,CAAC;EAC1E,MAAM,kBAAkB,SAAS,KAC9B,SACC,IAAI,IACF,KAAK,eACF,KAAK,SAAS,iBAAiB,UAAU,KAAK,CAAC,CAAC,CAChD,KAAK,OAAO;GACX,MAAM,MAAM,SAAS,QAAQ,GAAG;AAChC,OAAI,QAAQ,GACV,OAAM,IAAI,MACR,uBAAuB,KAAK,UAAU,SAAS,CAAC,4BAA4B,KAC7E;AAEH,UAAO;IACP,CACL,CACJ;EAED,MAAM,UAAU,CAAC,GAAG,SAAS,MAAM,CAAC;EACpC,MAAM,SAAqB,EAAE;EAE7B,MAAM,2BAAW,IAAI,KAAa;EAClC,IAAI,aAAa,QAAQ,MAAM,QAAQ,CAAC,SAAS,IAAI,IAAI,CAAC;AAC1D,SAAO,eAAe,KAAA,GAAW;GAC/B,MAAM,eAAe,CAAC,WAAW;AACjC,YAAS,IAAI,WAAW;GAExB,IAAI,wBAAwB,CAAC,WAAW;AACxC,UAAO,sBAAsB,QAAQ;IACnC,MAAM,uBAAO,IAAI,KAAa;AAC9B,SAAK,MAAM,YAAY,uBAAuB;KAC5C,MAAM,sBAAsB,gBAAgB;AAC5C,aAAQ,SAAS,QAAQ;AACvB,UAAI,QAAQ,YAAY,SAAS,IAAI,IAAI,CACvC;AAGF,UADgB,gBAAgB,KACpB,IAAI,SAAS,IAAI,oBAAoB,IAAI,IAAI,EAAE;AACzD,oBAAa,KAAK,IAAI;AACtB,YAAK,IAAI,IAAI;AACb,gBAAS,IAAI,IAAI;;OAEnB;;AAEJ,4BAAwB,CAAC,GAAG,KAAK;;AAGnC,UAAO,KAAK,CAAC,GAAG,aAAa,CAAC;AAC9B,gBAAa,QAAQ,MAAM,QAAQ,CAAC,SAAS,IAAI,IAAI,CAAC;;AAGxD,SAAO,OAAO,KAAK,UAAU,MAAM,KAAK,QAAQ,SAAS,KAAK,CAAC;;;CAIjE,OAAO,aAAa,MAAkD;EACpE,MAAM,aAAa,IAAI,IACrB,KAAK,SAAS,SAAS,KAAK,eAAe,CAAC,KAAK,SAAS,iBAAiB,UAAU,KAAK,CAAC,CAAC,CAC7F;AACD,SAAO,KAAK,QAAQ,SAAS,CAAC,WAAW,IAAI,iBAAiB,UAAU,KAAK,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"linker_columns.js","names":[],"sources":["../../../src/drivers/pframe/linker_columns.ts"],"sourcesContent":["import { canonicalizeJson, parseJson, type CanonicalizedJson } from \"../../json\";\nimport {\n Annotation,\n readAnnotationJson,\n type AxisSpec,\n type PColumnIdAndSpec,\n type AxisSpecNormalized,\n type AxisId,\n getAxisId,\n getNormalizedAxesList,\n getArrayFromAxisTree,\n getAxesTree,\n} from \"./spec/spec\";\n\ntype LinkerKey = CanonicalizedJson<AxisId[]>;\nexport type CompositeLinkerMap = Map<\n LinkerKey,\n {\n keyAxesSpec: AxisSpecNormalized[]; // axis specs - source for the key\n linkWith: Map<LinkerKey, PColumnIdAndSpec>; // for every axis (possibly in group with parents - available by linkers another axes and corresponding linkers)\n }\n>;\n\ninterface LinkersData {\n data: CompositeLinkerMap;\n}\nexport class LinkerMap implements LinkersData {\n /** Graph of linkers connected by axes (single or grouped by parents) */\n readonly data: CompositeLinkerMap;\n\n constructor(linkerMap: CompositeLinkerMap) {\n this.data = linkerMap;\n }\n\n get keys() {\n return this.data.keys();\n }\n\n get keyAxesIds() {\n return [...this.data.keys()].map(parseJson);\n }\n\n static fromColumns(columns: PColumnIdAndSpec[]) {\n const result: CompositeLinkerMap = new Map();\n for (const linker of columns.filter(\n (l) => !!readAnnotationJson(l.spec, Annotation.IsLinkerColumn),\n )) {\n const groups = LinkerMap.getAxesGroups(getNormalizedAxesList(linker.spec.axesSpec)); // split input axes into groups by parent links from annotation\n\n if (groups.length !== 2) {\n continue; // not a valid linker column\n }\n const [left, right] = groups;\n\n // In case of group:\n // A - C\n // \\_ B _ D\n // E/\n // put 2 variants as keys:\n // A - C\n // \\_ B _ D\n // and\n // E - B - D\n const leftKeyVariants: [LinkerKey, AxisSpecNormalized[]][] = LinkerMap.getAxesRoots(left).map(\n (axis) => {\n const axes = getArrayFromAxisTree(getAxesTree(axis));\n const key = canonicalizeJson(axes.map(getAxisId));\n return [key, axes];\n },\n );\n const rightKeyVariants: [LinkerKey, AxisSpecNormalized[]][] = LinkerMap.getAxesRoots(\n right,\n ).map((axis) => {\n const axes = getArrayFromAxisTree(getAxesTree(axis));\n const key = canonicalizeJson(axes.map(getAxisId));\n return [key, axes];\n });\n\n for (const [keyLeft, spec] of leftKeyVariants) {\n if (!result.has(keyLeft)) {\n result.set(keyLeft, { keyAxesSpec: spec, linkWith: new Map() });\n }\n }\n for (const [keyRight, spec] of rightKeyVariants) {\n if (!result.has(keyRight)) {\n result.set(keyRight, { keyAxesSpec: spec, linkWith: new Map() });\n }\n }\n for (const [keyRight] of rightKeyVariants) {\n for (const [keyLeft] of leftKeyVariants) {\n result.get(keyRight)?.linkWith.set(keyLeft, linker);\n }\n }\n }\n return new this(result);\n }\n\n /** Get all available nodes of linker graphs if start from sourceAxesKeys */\n searchAvailableAxesKeys(sourceAxesKeys: LinkerKey[]): Set<LinkerKey> {\n const startKeys = new Set(sourceAxesKeys);\n const allAvailableKeys = new Set<LinkerKey>();\n let nextKeys = sourceAxesKeys;\n while (nextKeys.length) {\n const next: LinkerKey[] = [];\n for (const key of nextKeys) {\n const node = this.data.get(key);\n if (!node) continue;\n for (const availableKey of node.linkWith.keys()) {\n if (!allAvailableKeys.has(availableKey) && !startKeys.has(availableKey)) {\n next.push(availableKey);\n allAvailableKeys.add(availableKey);\n }\n }\n }\n nextKeys = next;\n }\n return allAvailableKeys;\n }\n\n /** Get all linker columns that are necessary to reach endKey from startKey */\n searchLinkerPath(startKey: LinkerKey, endKey: LinkerKey): PColumnIdAndSpec[] {\n const previous: Record<LinkerKey, LinkerKey> = {};\n let nextIds = new Set([startKey]);\n const visited = new Set([startKey]);\n while (nextIds.size) {\n const next = new Set<LinkerKey>();\n for (const nextId of nextIds) {\n const node = this.data.get(nextId);\n if (!node) continue;\n for (const availableId of node.linkWith.keys()) {\n previous[availableId] = nextId;\n if (availableId === endKey) {\n const ids: LinkerKey[] = [];\n let current = endKey;\n while (previous[current] !== startKey) {\n ids.push(current);\n current = previous[current];\n }\n ids.push(current);\n // Edge (previous[id] -> id) is stored at the \"from\" node in one-directional map\n return ids.map((id: LinkerKey) => this.data.get(previous[id])!.linkWith.get(id)!);\n } else if (!visited.has(availableId)) {\n next.add(availableId);\n visited.add(availableId);\n }\n }\n }\n nextIds = next;\n }\n return [];\n }\n\n getLinkerColumnsForAxes({\n from: sourceAxes,\n to: targetAxes,\n throwWhenNoLinkExists = true,\n }: {\n from: AxisSpecNormalized[];\n to: AxisSpecNormalized[];\n throwWhenNoLinkExists?: boolean;\n }): PColumnIdAndSpec[] {\n // start keys - all possible keys in linker map using sourceAxes (for example, all axes of block's columns or all axes of columns in data-inputs)\n const startKeys: LinkerKey[] = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n\n return Array.from(\n new Map(\n LinkerMap.getAxesRoots(targetAxes)\n .map(LinkerMap.getLinkerKeyFromAxisSpec) // target keys contain all axes to be linked; if some of target axes has parents they must be in the key\n .flatMap((targetKey) => {\n const linkers = startKeys\n .map((startKey) => this.searchLinkerPath(startKey, targetKey))\n .reduce(\n (shortestPath, path) =>\n (shortestPath.length && shortestPath.length < path.length) || !path.length\n ? shortestPath\n : path,\n [] as PColumnIdAndSpec[],\n )\n .map((linker) => [linker.columnId, linker] as const);\n if (!linkers.length && throwWhenNoLinkExists) {\n throw Error(`Unable to find linker column for ${targetKey}`);\n }\n return linkers;\n }),\n ).values(),\n );\n }\n\n /** Get list of axisSpecs from keys of linker columns map */\n getAxesListFromKeysList(keys: LinkerKey[]): AxisSpecNormalized[] {\n return Array.from(\n new Map(\n keys\n .flatMap((key) => this.data.get(key)?.keyAxesSpec ?? [])\n .map((axis) => [canonicalizeJson(getAxisId(axis)), axis]),\n ).values(),\n );\n }\n\n /** Get axes of target axes that are impossible to be linked to source axes with current linker map */\n getNonLinkableAxes(\n sourceAxes: AxisSpecNormalized[],\n targetAxes: AxisSpecNormalized[],\n ): AxisSpecNormalized[] {\n const startKeys = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n // target keys contain all axes to be linked; if some of target axes has parents they must be in the key\n const targetKeys = targetAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n\n const axes = Array.from(\n new Map(\n targetAxes\n .filter((_targetAxis, idx) => {\n const targetKey = targetKeys[idx];\n return !startKeys.some((startKey) => this.searchLinkerPath(startKey, targetKey).length);\n })\n .flatMap((axis) =>\n getArrayFromAxisTree(getAxesTree(axis)).map((axis) => [\n canonicalizeJson(getAxisId(axis)),\n axis,\n ]),\n ),\n ).values(),\n );\n return axes;\n }\n\n /** Get all axes that can be connected to sourceAxes by linkers */\n getReachableByLinkersAxesFromAxesNormalized(\n sourceAxes: AxisSpecNormalized[],\n matchAxisIdFn?: (axisIdOfLinker: AxisId, axisIdOfSource: AxisId) => boolean,\n ): AxisSpecNormalized[] {\n let startKeys: CanonicalizedJson<AxisId[]>[] = [];\n\n if (matchAxisIdFn) {\n const sourceAxisIdsGrouped: AxisId[][] = sourceAxes.map((axis) =>\n getArrayFromAxisTree(getAxesTree(axis)).map(getAxisId),\n );\n for (const sourceAxisIdsGroup of sourceAxisIdsGrouped) {\n const matched = this.keyAxesIds.find((keyIds: AxisId[]) =>\n keyIds.every((linkerKeyAxisId) =>\n sourceAxisIdsGroup.find((sourceAxisId) => matchAxisIdFn(linkerKeyAxisId, sourceAxisId)),\n ),\n );\n if (matched) {\n startKeys.push(canonicalizeJson(matched));\n }\n }\n } else {\n startKeys = sourceAxes.map(LinkerMap.getLinkerKeyFromAxisSpec);\n }\n\n const availableKeys = this.searchAvailableAxesKeys(startKeys);\n return this.getAxesListFromKeysList([...availableKeys]);\n }\n\n getReachableByLinkersAxesFromAxes(\n sourceAxes: AxisSpec[],\n matchAxisIdFn?: (axisIdOfLinker: AxisId, axisIdOfSource: AxisId) => boolean,\n ): AxisSpecNormalized[] {\n return this.getReachableByLinkersAxesFromAxesNormalized(\n getNormalizedAxesList(sourceAxes),\n matchAxisIdFn,\n );\n }\n\n static getLinkerKeyFromAxisSpec(axis: AxisSpecNormalized): LinkerKey {\n return canonicalizeJson(getArrayFromAxisTree(getAxesTree(axis)).map(getAxisId));\n }\n\n /** Split array of axes into several arrays by parents: axes of one group are parents for each other.\n There are no order inside every group. */\n static getAxesGroups(axesSpec: AxisSpecNormalized[]): AxisSpecNormalized[][] {\n switch (axesSpec.length) {\n case 0:\n return [];\n case 1:\n return [[axesSpec[0]]];\n default:\n break;\n }\n\n const axisKeys = axesSpec.map((spec) => canonicalizeJson(getAxisId(spec)));\n const axisParentsIdxs = axesSpec.map(\n (spec) =>\n new Set(\n spec.parentAxesSpec\n .map((spec) => canonicalizeJson(getAxisId(spec)))\n .map((el) => {\n const idx = axisKeys.indexOf(el);\n if (idx === -1) {\n throw new Error(\n `malformed axesSpec: ${JSON.stringify(axesSpec)}, unable to locate parent ${el}`,\n );\n }\n return idx;\n }),\n ),\n );\n\n const allIdxs = [...axesSpec.keys()];\n const groups: number[][] = []; // groups of axis indexes\n\n const usedIdxs = new Set<number>();\n let nextFreeEl = allIdxs.find((idx) => !usedIdxs.has(idx));\n while (nextFreeEl !== undefined) {\n const currentGroup = [nextFreeEl];\n usedIdxs.add(nextFreeEl);\n\n let nextElsOfCurrentGroup = [nextFreeEl];\n while (nextElsOfCurrentGroup.length) {\n const next = new Set<number>();\n for (const groupIdx of nextElsOfCurrentGroup) {\n const groupElementParents = axisParentsIdxs[groupIdx];\n allIdxs.forEach((idx) => {\n if (idx === groupIdx || usedIdxs.has(idx)) {\n return;\n }\n const parents = axisParentsIdxs[idx];\n if (parents.has(groupIdx) || groupElementParents.has(idx)) {\n currentGroup.push(idx);\n next.add(idx);\n usedIdxs.add(idx);\n }\n });\n }\n nextElsOfCurrentGroup = [...next];\n }\n\n groups.push([...currentGroup]);\n nextFreeEl = allIdxs.find((idx) => !usedIdxs.has(idx));\n }\n\n return groups.map((group) => group.map((idx) => axesSpec[idx]));\n }\n\n /** Get all axes that are not parents of any other axis */\n static getAxesRoots(axes: AxisSpecNormalized[]): AxisSpecNormalized[] {\n const parentsSet = new Set(\n axes.flatMap((axis) => axis.parentAxesSpec).map((spec) => canonicalizeJson(getAxisId(spec))),\n );\n return axes.filter((axis) => !parentsSet.has(canonicalizeJson(getAxisId(axis))));\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-model-common",
3
- "version": "1.33.0",
3
+ "version": "1.34.0",
4
4
  "description": "Platforma SDK Model",
5
5
  "files": [
6
6
  "./dist/**/*",
@@ -26,8 +26,8 @@
26
26
  "@vitest/coverage-istanbul": "^4.1.3",
27
27
  "typescript": "~5.9.3",
28
28
  "vitest": "^4.1.3",
29
- "@milaboratories/build-configs": "2.0.0",
30
29
  "@milaboratories/ts-configs": "1.2.3",
30
+ "@milaboratories/build-configs": "2.0.0",
31
31
  "@milaboratories/ts-builder": "1.3.2"
32
32
  },
33
33
  "scripts": {
@@ -99,7 +99,7 @@ describe("Linker columns", () => {
99
99
  };
100
100
 
101
101
  testCase({ from: [axis2], to: [axis3], expected: [] });
102
- testCase({ from: [axis1], to: [axis2], expected: ["c12"] });
102
+ testCase({ from: [axis2], to: [axis1], expected: ["c12"] });
103
103
  testCase({ from: [axis1], to: [axis4], expected: [] });
104
104
  });
105
105
 
@@ -126,8 +126,8 @@ describe("Linker columns", () => {
126
126
  };
127
127
 
128
128
  testCase({
129
- from: getNormalizedAxesList([axisA, axisB]),
130
- to: getNormalizedAxesList([axisC]),
129
+ from: getNormalizedAxesList([axisC]),
130
+ to: getNormalizedAxesList([axisA, axisB]),
131
131
  expected: ["abc"],
132
132
  });
133
133
  });
@@ -239,13 +239,13 @@ describe("Linker columns", () => {
239
239
 
240
240
  const linkersMap = LinkerMap.fromColumns([linker1, linker2]);
241
241
 
242
- // Forward-only: from group2 we only reach group3 (no back edge to group1)
242
+ // Reversed edges: from group2 we reach group1 (no forward edge to group3)
243
243
  expect(
244
244
  new Set(
245
245
  linkersMap.getReachableByLinkersAxesFromAxesNormalized(group2Normalized).map((a) => a.name),
246
246
  ),
247
- ).toEqual(new Set([...group3].map((a) => a.name)));
248
- // Non-root axes (axisDn, axisBn) don't match linker map keys, so no forward reachability
247
+ ).toEqual(new Set(group1.map((a) => a.name)));
248
+ // Non-root axes (axisDn, axisBn) don't match linker map keys, so no reachability
249
249
  expect(linkersMap.getReachableByLinkersAxesFromAxesNormalized([axisDn])).toEqual([]);
250
250
  expect(linkersMap.getReachableByLinkersAxesFromAxesNormalized([axisBn])).toEqual([]);
251
251
  });
@@ -257,13 +257,17 @@ describe("Linker columns", () => {
257
257
  const axisC2 = makeTestAxis({ name: "c", parents: [axisB, axisA] });
258
258
  const axisD = makeTestAxis({ name: "d" });
259
259
 
260
- const [, , c1, c2] = getNormalizedAxesList([axisA, axisB, axisC1, axisC2, axisD]);
260
+ const [, , c1, c2, dn] = getNormalizedAxesList([axisA, axisB, axisC1, axisC2, axisD]);
261
261
  const linkerMap = LinkerMap.fromColumns([
262
262
  makeLinkerColumn({ name: "linker1", from: [axisA, axisB, axisC1], to: [axisD] }),
263
263
  ]);
264
264
 
265
- expect(linkerMap.getReachableByLinkersAxesFromAxesNormalized([c2])).not.toHaveLength(0);
266
- expect(linkerMap.getReachableByLinkersAxesFromAxesNormalized([c1])).not.toHaveLength(0);
265
+ expect(
266
+ linkerMap.getLinkerColumnsForAxes({ from: [dn], to: [c2], throwWhenNoLinkExists: false }),
267
+ ).not.toHaveLength(0);
268
+ expect(
269
+ linkerMap.getLinkerColumnsForAxes({ from: [dn], to: [c1], throwWhenNoLinkExists: false }),
270
+ ).not.toHaveLength(0);
267
271
  });
268
272
 
269
273
  test("Non-linkable axes", () => {
@@ -284,7 +288,7 @@ describe("Linker columns", () => {
284
288
  getNormalizedAxesList([axisA, axisB, axisC, axisE]),
285
289
  )
286
290
  .map((v) => v.name),
287
- ).toEqual(["a", "b", "c", "e"]);
291
+ ).toEqual(["a", "b", "e"]);
288
292
 
289
293
  expect(
290
294
  linkerMap
@@ -304,9 +308,9 @@ describe("Linker columns", () => {
304
308
  makeLinkerColumn({ name: "linker3", from: [axisC], to: [axisD] }),
305
309
  ]);
306
310
 
307
- expect(linkerMap.getReachableByLinkersAxesFromAxes([axisA])).toEqual(
308
- getNormalizedAxesList([axisB, axisC, axisD]),
311
+ expect(linkerMap.getReachableByLinkersAxesFromAxes([axisD])).toEqual(
312
+ getNormalizedAxesList([axisC, axisB, axisA]),
309
313
  );
310
- expect(linkerMap.getReachableByLinkersAxesFromAxes([axisD])).toEqual([]);
314
+ expect(linkerMap.getReachableByLinkersAxesFromAxes([axisA])).toEqual([]);
311
315
  });
312
316
  });
@@ -86,9 +86,9 @@ export class LinkerMap implements LinkersData {
86
86
  result.set(keyRight, { keyAxesSpec: spec, linkWith: new Map() });
87
87
  }
88
88
  }
89
- for (const [keyLeft] of leftKeyVariants) {
90
- for (const [keyRight] of rightKeyVariants) {
91
- result.get(keyLeft)?.linkWith.set(keyRight, linker);
89
+ for (const [keyRight] of rightKeyVariants) {
90
+ for (const [keyLeft] of leftKeyVariants) {
91
+ result.get(keyRight)?.linkWith.set(keyLeft, linker);
92
92
  }
93
93
  }
94
94
  }