Skip to content
Snippets Groups Projects
Commit 4fb9af89 authored by David Sehnal's avatar David Sehnal
Browse files

tables

parent a81464dd
No related branches found
No related tags found
No related merge requests found
......@@ -69,6 +69,27 @@ describe('table', () => {
expect(t.n.toArray()).toEqual(['row1', 'row2']);
});
it('ofArrays', () => {
const t = Table.ofArrays<typeof schema>(schema, {
x: [10, -1],
n: ['row1', 'row2'],
});
expect(t.x.toArray()).toEqual([10, -1]);
expect(t.n.toArray()).toEqual(['row1', 'row2']);
});
it('pickColumns', () => {
const t = Table.ofColumns<typeof schema>({
x: Column.ofArray({ array: [10, -1], type: Column.Type.int }),
n: Column.ofArray({ array: ['row1', 'row2'], type: Column.Type.str }),
});
const s = { x: Column.Type.int };
const picked = Table.pickColumns(s, t);
expect(picked._columns).toEqual(['x']);
expect(picked._rowCount).toEqual(2);
expect(picked.x.toArray()).toEqual([10, -1]);
});
it('sort', () => {
const t = Table.ofColumns<typeof schema>({
x: Column.ofArray({ array: [10, -1], type: Column.Type.int }),
......
......@@ -17,18 +17,24 @@ interface Column<T> {
}
namespace Column {
export type Type = typeof Type.str | typeof Type.int | typeof Type.float | Type.Vector | Type.Matrix
export type Type<T = any> = Type.Str | Type.Int | Type.Float | Type.Vector | Type.Matrix | Type.Aliased<T>
export namespace Type {
export const str = { T: '' as string, kind: 'str' as 'str' };
export const int = { T: 0 as number, kind: 'int' as 'int' };
export const float = { T: 0 as number, kind: 'float' as 'float' };
export type Str = { T: string, kind: 'str' }
export type Int = { T: number, kind: 'int' }
export type Float = { T: number, kind: 'float' }
export type Vector = { T: number[], dim: number, kind: 'vector' };
export type Matrix = { T: number[][], rows: number, cols: number, kind: 'matrix' };
export type Aliased<T> = { T: T } & { kind: 'str' | 'int' | 'float' }
export const str: Str = { T: '', kind: 'str' };
export const int: Int = { T: 0, kind: 'int' };
export const float: Float = { T: 0, kind: 'float' };
export function vector(dim: number): Vector { return { T: [] as number[], dim, kind: 'vector' }; }
export function matrix(rows: number, cols: number): Matrix { return { T: [] as number[][], rows, cols, kind: 'matrix' }; }
export function aliased<T>(t: Type): Aliased<T> { return t as any as Aliased<T>; }
}
export interface ToArrayParams {
......
......@@ -9,10 +9,21 @@ import { sortArray } from './sort'
type Table<Schema extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & Table.Columns<Schema>
/** An immutable table */
namespace Table {
export type Schema = { [field: string]: Column.Type }
export type Columns<S extends Schema> = { [C in keyof S]: Column<S[C]['T']> }
export type Row<S extends Schema> = { [C in keyof S]: S[C]['T'] }
export type Arrays<S extends Schema> = { [C in keyof S]: ArrayLike<S[C]['T']> }
export function pickColumns<S extends Schema, T extends S>(schema: S, table: Table<T>): Table<S> {
const ret = Object.create(null);
const keys = Object.keys(schema);
ret._rowCount = table._rowCount;
ret._columns = keys;
for (const k of keys) ret[k] = table[k];
return ret;
}
export function ofColumns<S extends Schema, R extends Table<S> = Table<S>>(columns: Columns<S>): R {
const _columns = Object.keys(columns);
......@@ -26,7 +37,7 @@ namespace Table {
const columns = Object.keys(schema);
ret._rowCount = rowCount;
ret._columns = columns;
for (const k of Object.keys(schema)) {
for (const k of columns) {
(ret as any)[k] = Column.ofLambda({
rowCount,
type: schema[k],
......@@ -37,6 +48,17 @@ namespace Table {
return ret as R;
}
export function ofArrays<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, arrays: Arrays<S>): R {
const ret = Object.create(null);
const columns = Object.keys(schema);
ret._rowCount = arrays[columns[0]].length;
ret._columns = columns;
for (const k of Object.keys(schema)) {
(ret as any)[k] = Column.ofArray({ array: arrays[k], type: schema[k] })
}
return ret as R;
}
/** Sort and return a new table */
export function sort<T extends Table<S>, S extends Schema>(table: T, cmp: (i: number, j: number) => number) {
const indices = new Int32Array(table._rowCount);
......
......@@ -5,7 +5,8 @@
*/
import Column from '../../../mol-base/collections/column'
import { Shape as mmCIF } from '../../../mol-io/reader/cif/schema/mmcif'
import Table from '../../../mol-base/collections/table'
import { Schema as mmCIF } from '../../../mol-io/reader/cif/schema/mmcif'
export interface ElementSymbol extends String { '@type': 'element-symbol' }
export function ElementSymbol(s: string): ElementSymbol {
......@@ -13,50 +14,55 @@ export function ElementSymbol(s: string): ElementSymbol {
return s.toUpperCase() as any;
}
type Key = { key: Column<number> }
type _Atoms = Pick<mmCIF['atom_site'],
| 'type_symbol'
| 'label_atom_id'
| 'auth_atom_id'
| 'label_alt_id'
| 'pdbx_formal_charge'
| 'occupancy'
| 'B_iso_or_equiv'>
& Key
export interface Atoms extends _Atoms {
source_row: Column<number>
}
export const AtomsSchema = {
type_symbol: Column.Type.aliased<ElementSymbol>(mmCIF.atom_site.type_symbol),
label_atom_id: mmCIF.atom_site.label_atom_id,
auth_atom_id: mmCIF.atom_site.auth_atom_id,
label_alt_id: mmCIF.atom_site.label_alt_id,
pdbx_formal_charge: mmCIF.atom_site.pdbx_formal_charge,
occupancy: mmCIF.atom_site.occupancy,
B_iso_or_equiv: mmCIF.atom_site.B_iso_or_equiv,
key: Column.Type.int,
source_row: Column.Type.int,
};
export interface Atoms extends Table<typeof AtomsSchema> { }
export const ResiduesSchema = {
group_PDB: mmCIF.atom_site.group_PDB,
label_comp_id: mmCIF.atom_site.label_comp_id,
auth_comp_id: mmCIF.atom_site.auth_comp_id,
label_seq_id: mmCIF.atom_site.label_seq_id,
auth_seq_id: mmCIF.atom_site.auth_seq_id,
pdbx_PDB_ins_code: mmCIF.atom_site.pdbx_PDB_ins_code,
type _Residues = Pick<mmCIF['atom_site'],
| 'group_PDB'
| 'label_comp_id'
| 'auth_comp_id'
| 'label_seq_id'
| 'auth_seq_id'
| 'pdbx_PDB_ins_code'>
& Key
export interface Residues extends _Residues { }
type _Chains = Pick<mmCIF['atom_site'],
| 'label_asym_id'
| 'auth_asym_id'
| 'auth_comp_id'
| 'label_entity_id'
| 'pdbx_PDB_model_num'>
& Key
export interface Chains extends _Chains {
enityDataIndex: Column<number>
key: Column.Type.int
};
export interface Residues extends Table<typeof AtomsSchema> { }
export const ChainsSchema = {
label_asym_id: mmCIF.atom_site.label_asym_id,
auth_asym_id: mmCIF.atom_site.auth_asym_id,
auth_comp_id: mmCIF.atom_site.auth_comp_id,
label_entity_id: mmCIF.atom_site.label_entity_id,
pdbx_PDB_model_num: mmCIF.atom_site.pdbx_PDB_model_num,
key: Column.Type.int,
entityIndex: Column.Type.int
}
type _EntityData = mmCIF['entity']
export interface EntityData extends _EntityData { }
export interface Chains extends Table<typeof ChainsSchema> { }
export const EntitySchema = mmCIF['entity']
export interface Entities extends Table<typeof EntitySchema> { }
export interface Macromolecule {
atoms: Atoms,
residues: Residues,
chains: Chains,
entityData: EntityData
entities: Entities
}
export default Macromolecule
\ No newline at end of file
......@@ -21,7 +21,7 @@ const testBlock = Data.Block({
}, 'test');
namespace TestSchema {
export const atoms = { x: Schema.Field.int(), name: Schema.Field.str() }
export const atoms = { x: Schema.Types.int, name: Schema.Types.str }
export const schema = { atoms }
}
......
......@@ -6,93 +6,56 @@
import * as Data from './data-model'
import Column, { createAndFillArray } from '../../../mol-base/collections/column'
/**
* A schema defines the shape of categories and fields.
*
* @example:
* const atom_site = {
* '@alias': '_atom_site',
* label_atom_id: Field.str(),
* Cartn_x: Field.float(),
* Cartn_y: Field.float(),
* Cartn_z: Field.float(),
* }
*
* const mmCIF = { atom_site };
*/
//////////////////////////////////////////////
import Table from '../../../mol-base/collections/table'
export function toTypedFrame<Schema extends FrameSchema, Frame extends TypedFrame<Schema> = TypedFrame<Schema>>(schema: Schema, frame: Data.Frame): Frame {
return createTypedFrame(schema, frame) as Frame;
}
export function toTypedCategory<Schema extends CategorySchema>(schema: Schema, category: Data.Category): TypedCategory<Schema> {
return new _TypedCategory(category, schema, true) as TypedCategory<any>;
export function toTable<Schema extends Table.Schema, R extends Table<Schema> = Table<Schema>>(schema: Schema, category: Data.Category): R {
return new _TypedCategory(category, schema, true) as any;
}
export type FrameSchema = { [category: string]: CategorySchema }
export type TypedFrameShape<Schema extends FrameSchema> = { [C in keyof Schema]: TypedCategoryShape<Schema[C]> }
export const Types = Column.Type
export type FrameSchema = { [category: string]: Table.Schema }
export type TypedFrame<Schema extends FrameSchema> = {
readonly _header?: string,
readonly _frame: Data.Frame
} & { [C in keyof Schema]: TypedCategory<Schema[C]> }
export type CategorySchema = { [field: string]: Field.Schema<any> }
export type TypedCategoryShape<Schema extends CategorySchema> = { [F in keyof Schema]: Column<Schema[F]['T']> }
export type TypedCategory<Schema extends CategorySchema> = {
readonly _rowCount: number,
readonly _isDefined: boolean,
readonly _category: Data.Category
} & { [F in keyof Schema]: Column<Schema[F]['T']> }
export namespace Field {
export interface Schema<T> { T: T, ctor: (field: Data.Field, category: Data.Category, key: string) => Column<T>, undefinedField: (c: number) => Data.Field, alias?: string };
export interface Spec { undefinedField?: (c: number) => Data.Field, alias?: string }
export function alias(name: string): Schema<any> { return { alias: name } as any; }
export function str(spec?: Spec) { return createSchema(spec, Str); }
export function int(spec?: Spec) { return createSchema(spec, Int); }
export function float(spec?: Spec) { return createSchema(spec, Float); }
export function vector(rows: number, spec?: Spec) { return createSchema(spec, Vector(rows)); }
export function matrix(rows: number, cols: number, spec?: Spec) { return createSchema(spec, Matrix(rows, cols)); }
function create<T>(type: Column.Type, field: Data.Field, value: (row: number) => T, toArray: Column<T>['toArray']): Column<T> {
return {
'@type': type,
'@array': field['@array'],
isDefined: field.isDefined,
rowCount: field.rowCount,
value,
valueKind: field.valueKind,
areValuesEqual: field.areValuesEqual,
toArray
};
}
function Str(field: Data.Field) { return create(Column.Type.str, field, field.str, field.toStringArray); }
function Int(field: Data.Field) { return create(Column.Type.int, field, field.int, field.toIntArray); }
function Float(field: Data.Field) { return create(Column.Type.float, field, field.float, field.toFloatArray); }
function Vector(rows: number) {
return function(field: Data.Field, category: Data.Category, key: string) {
const value = (row: number) => Data.getVector(category, key, rows, row);
return create(Column.Type.vector(rows), field, value, params => createAndFillArray(field.rowCount, value, params));
} & { [C in keyof Schema]: Table<Schema[C]> }
type ColumnCtor = (field: Data.Field, category: Data.Category, key: string) => Column<any>
function getColumnCtor(t: Column.Type): ColumnCtor {
switch (t.kind) {
case 'str': return (f, c, k) => createColumn(Column.Type.str, f, f.str, f.toStringArray);
case 'int': return (f, c, k) => createColumn(Column.Type.int, f, f.int, f.toIntArray);
case 'float': return (f, c, k) => createColumn(Column.Type.float, f, f.float, f.toFloatArray);
case 'vector': return (f, c, k) => {
const dim = t.dim;
const value = (row: number) => Data.getVector(c, k, dim, row);
return createColumn(t, f, value, params => createAndFillArray(f.rowCount, value, params));
}
}
function Matrix(rows: number, cols: number) {
return function(field: Data.Field, category: Data.Category, key: string) {
const value = (row: number) => Data.getMatrix(category, key, rows, cols, row);
return create(Column.Type.matrix(rows, cols), field, value, params => createAndFillArray(field.rowCount, value, params));
case 'matrix': return (f, c, k) => {
const rows = t.rows, cols = t.cols;
const value = (row: number) => Data.getMatrix(c, k, rows, cols, row);
return createColumn(t, f, value, params => createAndFillArray(f.rowCount, value, params));
}
}
}
// spec argument is to allow for specialised implementation for undefined fields
function createSchema<T>(spec: Spec | undefined, ctor: (field: Data.Field, category: Data.Category, key: string) => Column<T>): Schema<T> {
return { T: 0 as any, ctor, undefinedField: (spec && spec.undefinedField) || Data.DefaultUndefinedField, alias: spec && spec.alias };
}
function createColumn<T>(type: Column.Type, field: Data.Field, value: (row: number) => T, toArray: Column<T>['toArray']): Column<T> {
return {
'@type': type,
'@array': field['@array'],
isDefined: field.isDefined,
rowCount: field.rowCount,
value,
valueKind: field.valueKind,
areValuesEqual: field.areValuesEqual,
toArray
};
}
class _TypedFrame implements TypedFrame<any> { // tslint:disable-line:class-name
......@@ -104,19 +67,21 @@ class _TypedFrame implements TypedFrame<any> { // tslint:disable-line:class-name
}
}
class _TypedCategory implements TypedCategory<any> { // tslint:disable-line:class-name
class _TypedCategory implements Table<any> { // tslint:disable-line:class-name
_rowCount = this._category.rowCount;
constructor(public _category: Data.Category, schema: CategorySchema, public _isDefined: boolean) {
const fieldKeys = Object.keys(schema).filter(k => k !== '@alias');
_columns: ReadonlyArray<string>;
constructor(public _category: Data.Category, schema: Table.Schema, public _isDefined: boolean) {
const fieldKeys = Object.keys(schema);
this._columns = fieldKeys;
const cache = Object.create(null);
for (const k of fieldKeys) {
const s = schema[k];
const cType = schema[k];
const ctor = getColumnCtor(cType);
Object.defineProperty(this, k, {
get: function() {
if (cache[k]) return cache[k];
const name = s.alias || k;
const field = _category.getField(name) || s.undefinedField(_category.rowCount);
cache[k] = s.ctor(field, _category, name);
const field = _category.getField(k);
cache[k] = !!field ? ctor(field, _category, k) : Column.Undefined(_category.rowCount, cType);
return cache[k];
},
enumerable: true,
......@@ -130,9 +95,7 @@ function createTypedFrame(schema: FrameSchema, frame: Data.Frame): any {
return new _TypedFrame(frame, schema);
}
function createTypedCategory(key: string, schema: CategorySchema, frame: Data.Frame) {
const alias = (schema['@alias'] && schema['@alias'].alias) || key;
const name = alias[0] === '_' ? alias : '_' + alias;
const cat = frame.categories[name];
function createTypedCategory(key: string, schema: Table.Schema, frame: Data.Frame) {
const cat = frame.categories[key[0] === '_' ? key : '_' + key];
return new _TypedCategory(cat || Data.Category.Empty, schema, !!cat);
}
\ No newline at end of file
......@@ -4,10 +4,10 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Field, TypedFrame } from '../schema'
import { Types, TypedFrame } from '../schema'
const str = Field.str()
const float = Field.float()
const str = Types.str
const float = Types.float
const datablock = {
id: str,
......@@ -58,7 +58,7 @@ const item_units_conversion = {
// TODO save frame dic schema
const dic = {
export const Schema = {
datablock,
dictionary,
dictionary_history,
......@@ -69,5 +69,7 @@ const dic = {
item_units_conversion
}
type dic = TypedFrame<typeof dic>
export default dic
export interface Frame extends TypedFrame<typeof Schema> { }
// type dic = TypedFrame<typeof dic>
//export default dic
......@@ -4,11 +4,11 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Field, TypedFrame, TypedFrameShape } from '../schema'
import { Types, TypedFrame } from '../schema'
const str = Field.str();
const int = Field.int();
const float = Field.float();
const str = Types.str;
const int = Types.int;
const float = Types.float;
const entry = {
id: str
......@@ -18,7 +18,7 @@ type EntityType = 'polymer' | 'non-polymer' | 'water'
const entity = {
id: str,
type: str as Field.Schema<EntityType>,
type: Types.aliased<EntityType>(str),
src_method: str,
pdbx_description: str,
formula_weight: float,
......@@ -48,8 +48,8 @@ const cell = {
const symmetry = {
entry_id: str,
space_group_name_HM: Field.str({ alias: 'space_group_name_H-M' }),
pdbx_full_space_group_name_HM: Field.str({ alias: 'pdbx_full_space_group_name_H-M' }),
'space_group_name_H-M': str,
'pdbx_full_space_group_name_H': str,
cell_setting: str,
Int_Tables_number: int,
space_group_name_Hall: str
......@@ -117,7 +117,7 @@ type BondValueOrder =
const struct_conn = {
id: str,
conn_type_id: str as Field.Schema<StructConnTypeId>,
conn_type_id: Types.aliased<StructConnTypeId>(str),
pdbx_PDB_id: str,
ptnr1_label_asym_id: str,
ptnr1_label_comp_id: str,
......@@ -148,11 +148,11 @@ const struct_conn = {
pdbx_ptnr3_PDB_ins_code: str,
details: str,
pdbx_dist_value: float,
pdbx_value_order: str as Field.Schema<BondValueOrder>
pdbx_value_order: Types.aliased<BondValueOrder>(str)
}
const struct_conn_type = {
id: str as Field.Schema<StructConnTypeId>,
id: Types.aliased<StructConnTypeId>(str),
criteria: str,
reference: str
}
......@@ -161,10 +161,10 @@ const chem_comp_bond = {
comp_id: str,
pdbx_stereo_config: str,
pdbx_ordinal: int,
pdbx_aromatic_flag: str as Field.Schema<'Y' | 'N'>,
pdbx_aromatic_flag: Types.aliased<'Y' | 'N'>(str),
atom_id_1: str,
atom_id_2: str,
value_order: str as Field.Schema<BondValueOrder>
value_order: Types.aliased<BondValueOrder>(str)
}
const pdbx_struct_assembly = {
......@@ -186,8 +186,8 @@ const pdbx_struct_oper_list = {
type: str,
name: str,
symmetry_operation: str,
matrix: Field.matrix(3, 3),
vector: Field.vector(3)
matrix: Types.matrix(3, 3),
vector: Types.vector(3)
}
const pdbx_struct_mod_residue = {
......@@ -245,5 +245,4 @@ export const Schema = {
atom_site
};
export interface Frame extends TypedFrame<typeof Schema> { }
export interface Shape extends TypedFrameShape<typeof Schema> { }
\ No newline at end of file
export interface Frame extends TypedFrame<typeof Schema> { }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment