@aleph-ai/tinyaleph 1.2.0 → 1.3.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.
Files changed (35) hide show
  1. package/README.md +187 -2
  2. package/backends/bioinformatics/binding.js +503 -0
  3. package/backends/bioinformatics/dna-computing.js +664 -0
  4. package/backends/bioinformatics/encoding.js +339 -0
  5. package/backends/bioinformatics/folding.js +454 -0
  6. package/backends/bioinformatics/genetic-code.js +269 -0
  7. package/backends/bioinformatics/index.js +522 -0
  8. package/backends/bioinformatics/transcription.js +221 -0
  9. package/backends/bioinformatics/translation.js +264 -0
  10. package/backends/index.js +25 -1
  11. package/core/compound.js +532 -0
  12. package/core/hilbert.js +454 -1
  13. package/core/index.js +106 -12
  14. package/core/inference.js +605 -0
  15. package/core/resonance.js +245 -616
  16. package/core/symbols/archetypes.js +478 -0
  17. package/core/symbols/base.js +302 -0
  18. package/core/symbols/elements.js +487 -0
  19. package/core/symbols/hieroglyphs.js +303 -0
  20. package/core/symbols/iching.js +471 -0
  21. package/core/symbols/index.js +77 -0
  22. package/core/symbols/tarot.js +211 -0
  23. package/core/symbols.js +22 -0
  24. package/docs/design/BIOINFORMATICS_BACKEND_DESIGN.md +493 -0
  25. package/docs/guide/06-symbolic-ai.md +370 -0
  26. package/docs/guide/README.md +2 -1
  27. package/docs/reference/05-symbolic-ai.md +570 -0
  28. package/docs/reference/06-bioinformatics.md +546 -0
  29. package/docs/reference/README.md +32 -2
  30. package/docs/theory/11-prgraph-memory.md +559 -0
  31. package/docs/theory/12-resonant-attention.md +661 -0
  32. package/modular.js +33 -1
  33. package/package.json +1 -1
  34. package/physics/index.js +16 -0
  35. package/physics/kuramoto-coupled-ladder.js +603 -0
@@ -0,0 +1,664 @@
1
+ /**
2
+ * DNA Computing Module
3
+ *
4
+ * Implements DNA-based computation using strand displacement reactions
5
+ * and molecular logic gates. Based on the prime-resonance paradigm where:
6
+ *
7
+ * - DNA strands are prime sequences
8
+ * - Hybridization = prime product (complementary bases multiply)
9
+ * - Strand displacement = prime substitution transform
10
+ * - Logic gates = prime-based conditional operations
11
+ *
12
+ * References:
13
+ * - Seelig et al. (2006) - DNA logic circuits
14
+ * - Zhang & Winfree (2009) - Strand displacement cascades
15
+ * - Qian & Winfree (2011) - Seesaw gates
16
+ */
17
+
18
+ const {
19
+ NUCLEOTIDE_PRIMES,
20
+ PRIME_COMPLEMENTS,
21
+ encodeDNA,
22
+ decodeDNA
23
+ } = require('./encoding');
24
+
25
+ // ============================================================================
26
+ // DNA Strand Representation
27
+ // ============================================================================
28
+
29
+ /**
30
+ * DNAStrand - represents a single-stranded DNA molecule
31
+ */
32
+ class DNAStrand {
33
+ constructor(sequence, options = {}) {
34
+ this.sequence = sequence.toUpperCase();
35
+ this.primes = encodeDNA(this.sequence);
36
+ this.name = options.name || '';
37
+ this.concentration = options.concentration || 1.0;
38
+ this.toehold = options.toehold || null; // Toehold region for strand displacement
39
+ }
40
+
41
+ /**
42
+ * Get the Watson-Crick complement
43
+ */
44
+ complement() {
45
+ const compSeq = this.sequence.split('').map(n => {
46
+ const comps = { 'A': 'T', 'T': 'A', 'G': 'C', 'C': 'G' };
47
+ return comps[n] || n;
48
+ }).join('');
49
+
50
+ return new DNAStrand(compSeq, { name: `${this.name}'` });
51
+ }
52
+
53
+ /**
54
+ * Get the reverse complement (for double helix formation)
55
+ */
56
+ reverseComplement() {
57
+ return new DNAStrand(
58
+ this.complement().sequence.split('').reverse().join(''),
59
+ { name: `${this.name}*` }
60
+ );
61
+ }
62
+
63
+ /**
64
+ * Calculate binding affinity (free energy) with another strand
65
+ * Uses prime resonance for thermodynamic estimation
66
+ */
67
+ bindingAffinity(other) {
68
+ const minLen = Math.min(this.primes.length, other.primes.length);
69
+ let affinity = 0;
70
+ let matches = 0;
71
+
72
+ for (let i = 0; i < minLen; i++) {
73
+ const p1 = this.primes[i];
74
+ const p2 = other.primes[i];
75
+
76
+ // Perfect complement: product is fixed (14 for A-T, 33 for G-C)
77
+ if (PRIME_COMPLEMENTS[p1] === p2) {
78
+ affinity += 1.0;
79
+ matches++;
80
+ } else if (p1 === p2) {
81
+ // Same base: mismatch penalty
82
+ affinity -= 0.5;
83
+ }
84
+ }
85
+
86
+ return {
87
+ affinity,
88
+ matchFraction: matches / minLen,
89
+ meltingTemp: this.estimateMeltingTemp(matches, minLen)
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Estimate melting temperature using nearest-neighbor model
95
+ */
96
+ estimateMeltingTemp(matches, length) {
97
+ // Simplified Tm estimation
98
+ // Real calculation would use ΔH and ΔS from nearest-neighbor parameters
99
+ const gcContent = (this.sequence.match(/[GC]/g) || []).length / length;
100
+ const Tm = 64.9 + 41 * (gcContent - 0.5) + 16.6 * Math.log10(0.05);
101
+ return Tm * (matches / length); // Adjust for mismatches
102
+ }
103
+
104
+ /**
105
+ * Check if this strand can displace another from a duplex
106
+ */
107
+ canDisplace(incumbent, template) {
108
+ // Strand displacement requires:
109
+ // 1. A toehold region on the template
110
+ // 2. Higher affinity than incumbent
111
+
112
+ const myAffinity = this.bindingAffinity(template);
113
+ const theirAffinity = incumbent.bindingAffinity(template);
114
+
115
+ return myAffinity.affinity > theirAffinity.affinity;
116
+ }
117
+
118
+ /**
119
+ * Prime product representation (for hybridized duplex)
120
+ */
121
+ primeProduct(other) {
122
+ const minLen = Math.min(this.primes.length, other.primes.length);
123
+ return this.primes.slice(0, minLen).map((p, i) => p * other.primes[i]);
124
+ }
125
+
126
+ /**
127
+ * Get length
128
+ */
129
+ get length() {
130
+ return this.sequence.length;
131
+ }
132
+
133
+ toString() {
134
+ return `5'-${this.sequence}-3'`;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * DNADuplex - represents a double-stranded DNA molecule
140
+ */
141
+ class DNADuplex {
142
+ constructor(strand1, strand2, options = {}) {
143
+ this.strand1 = strand1; // Top strand (5' → 3')
144
+ this.strand2 = strand2; // Bottom strand (3' → 5')
145
+ this.name = options.name || '';
146
+ this.toeholdRegion = options.toehold || null;
147
+ }
148
+
149
+ /**
150
+ * Check if duplex is stable (complementary)
151
+ */
152
+ isStable() {
153
+ const affinity = this.strand1.bindingAffinity(this.strand2.reverseComplement());
154
+ return affinity.matchFraction > 0.8;
155
+ }
156
+
157
+ /**
158
+ * Prime signature of the duplex
159
+ */
160
+ get primeSignature() {
161
+ return this.strand1.primeProduct(this.strand2.reverseComplement());
162
+ }
163
+ }
164
+
165
+ // ============================================================================
166
+ // DNA Logic Gates
167
+ // ============================================================================
168
+
169
+ /**
170
+ * AND Gate
171
+ * Output is high only when both inputs are present
172
+ *
173
+ * Implementation: Two-input strand displacement
174
+ * Input1 + Input2 + Gate → Output + Waste
175
+ */
176
+ class ANDGate {
177
+ constructor(options = {}) {
178
+ this.name = options.name || 'AND';
179
+ this.threshold = options.threshold || 0.5;
180
+
181
+ // Gate strands (templates)
182
+ this.input1Binding = options.input1Binding || 'ATCGATCG';
183
+ this.input2Binding = options.input2Binding || 'GCTAGCTA';
184
+ this.outputSequence = options.output || 'TTCCAAGG';
185
+
186
+ // Convert to prime representation
187
+ this.input1Primes = encodeDNA(this.input1Binding);
188
+ this.input2Primes = encodeDNA(this.input2Binding);
189
+ this.outputPrimes = encodeDNA(this.outputSequence);
190
+ }
191
+
192
+ /**
193
+ * Evaluate gate with given input concentrations
194
+ */
195
+ evaluate(input1Conc, input2Conc) {
196
+ // Both inputs must exceed threshold
197
+ const active = input1Conc >= this.threshold && input2Conc >= this.threshold;
198
+ const outputConc = active ? Math.min(input1Conc, input2Conc) : 0;
199
+
200
+ return {
201
+ output: active,
202
+ concentration: outputConc,
203
+ outputPrimes: active ? this.outputPrimes : []
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Prime-based evaluation
209
+ */
210
+ evaluatePrimes(input1Primes, input2Primes) {
211
+ // Check if inputs match expected patterns
212
+ const match1 = this.primesMatch(input1Primes, this.input1Primes);
213
+ const match2 = this.primesMatch(input2Primes, this.input2Primes);
214
+
215
+ return {
216
+ output: match1 && match2,
217
+ outputPrimes: (match1 && match2) ? this.outputPrimes : [],
218
+ matchScores: { input1: match1, input2: match2 }
219
+ };
220
+ }
221
+
222
+ primesMatch(primes1, primes2, threshold = 0.8) {
223
+ if (primes1.length !== primes2.length) return false;
224
+ const matches = primes1.filter((p, i) => p === primes2[i]).length;
225
+ return matches / primes1.length >= threshold;
226
+ }
227
+ }
228
+
229
+ /**
230
+ * OR Gate
231
+ * Output is high when either input is present
232
+ */
233
+ class ORGate {
234
+ constructor(options = {}) {
235
+ this.name = options.name || 'OR';
236
+ this.threshold = options.threshold || 0.5;
237
+
238
+ this.input1Binding = options.input1Binding || 'ATCGATCG';
239
+ this.input2Binding = options.input2Binding || 'GCTAGCTA';
240
+ this.outputSequence = options.output || 'TTCCAAGG';
241
+
242
+ this.input1Primes = encodeDNA(this.input1Binding);
243
+ this.input2Primes = encodeDNA(this.input2Binding);
244
+ this.outputPrimes = encodeDNA(this.outputSequence);
245
+ }
246
+
247
+ evaluate(input1Conc, input2Conc) {
248
+ const active = input1Conc >= this.threshold || input2Conc >= this.threshold;
249
+ const outputConc = active ? Math.max(input1Conc, input2Conc) : 0;
250
+
251
+ return {
252
+ output: active,
253
+ concentration: outputConc,
254
+ outputPrimes: active ? this.outputPrimes : []
255
+ };
256
+ }
257
+
258
+ evaluatePrimes(input1Primes, input2Primes) {
259
+ const match1 = this.primesMatch(input1Primes, this.input1Primes);
260
+ const match2 = this.primesMatch(input2Primes, this.input2Primes);
261
+
262
+ return {
263
+ output: match1 || match2,
264
+ outputPrimes: (match1 || match2) ? this.outputPrimes : [],
265
+ matchScores: { input1: match1, input2: match2 }
266
+ };
267
+ }
268
+
269
+ primesMatch(primes1, primes2, threshold = 0.8) {
270
+ if (primes1.length !== primes2.length) return false;
271
+ const matches = primes1.filter((p, i) => p === primes2[i]).length;
272
+ return matches / primes1.length >= threshold;
273
+ }
274
+ }
275
+
276
+ /**
277
+ * NOT Gate (Inverter)
278
+ * Output is high when input is absent
279
+ */
280
+ class NOTGate {
281
+ constructor(options = {}) {
282
+ this.name = options.name || 'NOT';
283
+ this.threshold = options.threshold || 0.5;
284
+ this.decayRate = options.decayRate || 0.1;
285
+
286
+ this.inputBinding = options.inputBinding || 'ATCGATCG';
287
+ this.outputSequence = options.output || 'TTCCAAGG';
288
+
289
+ this.inputPrimes = encodeDNA(this.inputBinding);
290
+ this.outputPrimes = encodeDNA(this.outputSequence);
291
+
292
+ // NOT gate has a constitutive output that gets suppressed by input
293
+ this.constitutiveOutput = 1.0;
294
+ }
295
+
296
+ evaluate(inputConc) {
297
+ const active = inputConc < this.threshold;
298
+ const outputConc = active ? this.constitutiveOutput * (1 - inputConc) : 0;
299
+
300
+ return {
301
+ output: active,
302
+ concentration: outputConc,
303
+ outputPrimes: active ? this.outputPrimes : []
304
+ };
305
+ }
306
+
307
+ evaluatePrimes(inputPrimes) {
308
+ const match = this.primesMatch(inputPrimes, this.inputPrimes);
309
+
310
+ return {
311
+ output: !match,
312
+ outputPrimes: !match ? this.outputPrimes : [],
313
+ matchScore: match
314
+ };
315
+ }
316
+
317
+ primesMatch(primes1, primes2, threshold = 0.8) {
318
+ if (!primes1 || primes1.length === 0) return false;
319
+ if (primes1.length !== primes2.length) return false;
320
+ const matches = primes1.filter((p, i) => p === primes2[i]).length;
321
+ return matches / primes1.length >= threshold;
322
+ }
323
+ }
324
+
325
+ /**
326
+ * NAND Gate (Universal gate)
327
+ */
328
+ class NANDGate {
329
+ constructor(options = {}) {
330
+ this.andGate = new ANDGate(options);
331
+ this.notGate = new NOTGate({ ...options, inputBinding: options.output });
332
+ this.name = options.name || 'NAND';
333
+ }
334
+
335
+ evaluate(input1Conc, input2Conc) {
336
+ const andResult = this.andGate.evaluate(input1Conc, input2Conc);
337
+ return this.notGate.evaluate(andResult.concentration);
338
+ }
339
+ }
340
+
341
+ // ============================================================================
342
+ // Strand Displacement Reactions
343
+ // ============================================================================
344
+
345
+ /**
346
+ * StrandDisplacementReaction
347
+ * Models toehold-mediated strand displacement
348
+ */
349
+ class StrandDisplacementReaction {
350
+ constructor(options = {}) {
351
+ this.template = options.template; // The template strand
352
+ this.incumbent = options.incumbent; // Currently bound strand
353
+ this.invader = options.invader; // Displacing strand
354
+ this.toehold = options.toehold || 6; // Toehold length in nucleotides
355
+ this.rate = options.rate || 1e6; // Rate constant (M^-1 s^-1)
356
+ }
357
+
358
+ /**
359
+ * Calculate displacement rate based on toehold binding
360
+ */
361
+ calculateRate() {
362
+ // Rate depends on toehold binding strength
363
+ const toeholdPrimes = this.template.primes.slice(0, this.toehold);
364
+ const invaderToeholPrimes = this.invader.primes.slice(-this.toehold);
365
+
366
+ let bindingStrength = 0;
367
+ for (let i = 0; i < this.toehold; i++) {
368
+ if (PRIME_COMPLEMENTS[toeholdPrimes[i]] === invaderToeholPrimes[i]) {
369
+ bindingStrength += 1;
370
+ }
371
+ }
372
+
373
+ // Rate scales exponentially with toehold binding
374
+ return this.rate * Math.exp(bindingStrength - this.toehold);
375
+ }
376
+
377
+ /**
378
+ * Simulate displacement reaction
379
+ */
380
+ simulate(time, dt = 0.001) {
381
+ const rate = this.calculateRate();
382
+ const steps = Math.floor(time / dt);
383
+
384
+ let invaderConc = this.invader.concentration;
385
+ let incumbentConc = this.incumbent.concentration;
386
+ let templateConc = this.template.concentration;
387
+
388
+ const history = [];
389
+
390
+ for (let i = 0; i < steps; i++) {
391
+ // Displacement kinetics (simplified second-order)
392
+ const dDisplacement = rate * invaderConc * templateConc * dt;
393
+
394
+ invaderConc -= dDisplacement;
395
+ incumbentConc += dDisplacement;
396
+
397
+ history.push({
398
+ time: i * dt,
399
+ invader: invaderConc,
400
+ incumbent: incumbentConc,
401
+ displaced: this.incumbent.concentration - incumbentConc
402
+ });
403
+ }
404
+
405
+ return {
406
+ finalInvaderConc: invaderConc,
407
+ finalIncumbentConc: incumbentConc,
408
+ displacementFraction: 1 - invaderConc / this.invader.concentration,
409
+ history
410
+ };
411
+ }
412
+ }
413
+
414
+ // ============================================================================
415
+ // DNA Circuit
416
+ // ============================================================================
417
+
418
+ /**
419
+ * DNACircuit - A network of connected DNA logic gates
420
+ */
421
+ class DNACircuit {
422
+ constructor(name = 'circuit') {
423
+ this.name = name;
424
+ this.gates = new Map();
425
+ this.connections = [];
426
+ this.inputs = new Map();
427
+ this.outputs = new Map();
428
+ }
429
+
430
+ /**
431
+ * Add a gate to the circuit
432
+ */
433
+ addGate(id, gate) {
434
+ this.gates.set(id, gate);
435
+ return this;
436
+ }
437
+
438
+ /**
439
+ * Connect gate output to another gate input
440
+ */
441
+ connect(fromGateId, toGateId, inputSlot = 1) {
442
+ this.connections.push({
443
+ from: fromGateId,
444
+ to: toGateId,
445
+ slot: inputSlot
446
+ });
447
+ return this;
448
+ }
449
+
450
+ /**
451
+ * Set circuit input
452
+ */
453
+ setInput(inputId, value) {
454
+ this.inputs.set(inputId, value);
455
+ return this;
456
+ }
457
+
458
+ /**
459
+ * Evaluate the circuit
460
+ */
461
+ evaluate() {
462
+ const gateOutputs = new Map();
463
+
464
+ // Topological sort to determine evaluation order
465
+ const order = this.topologicalSort();
466
+
467
+ // Evaluate gates in order
468
+ for (const gateId of order) {
469
+ const gate = this.gates.get(gateId);
470
+
471
+ // Get inputs for this gate
472
+ const inputs = this.getGateInputs(gateId, gateOutputs);
473
+
474
+ // Evaluate gate
475
+ let result;
476
+ if (gate instanceof ANDGate || gate instanceof ORGate || gate instanceof NANDGate) {
477
+ result = gate.evaluate(inputs[0] || 0, inputs[1] || 0);
478
+ } else if (gate instanceof NOTGate) {
479
+ result = gate.evaluate(inputs[0] || 0);
480
+ }
481
+
482
+ gateOutputs.set(gateId, result);
483
+ }
484
+
485
+ return {
486
+ outputs: Object.fromEntries(gateOutputs),
487
+ finalOutput: gateOutputs.get([...this.gates.keys()].pop())
488
+ };
489
+ }
490
+
491
+ /**
492
+ * Get inputs for a gate from circuit inputs or upstream gate outputs
493
+ */
494
+ getGateInputs(gateId, gateOutputs) {
495
+ const inputs = [
496
+ this.inputs.get(`${gateId}_in1`),
497
+ this.inputs.get(`${gateId}_in2`)
498
+ ];
499
+
500
+ // Check for connections from other gates
501
+ for (const conn of this.connections) {
502
+ if (conn.to === gateId) {
503
+ const upstreamOutput = gateOutputs.get(conn.from);
504
+ if (upstreamOutput) {
505
+ inputs[conn.slot - 1] = upstreamOutput.concentration;
506
+ }
507
+ }
508
+ }
509
+
510
+ return inputs;
511
+ }
512
+
513
+ /**
514
+ * Topological sort for gate evaluation order
515
+ */
516
+ topologicalSort() {
517
+ const visited = new Set();
518
+ const order = [];
519
+ const gateIds = [...this.gates.keys()];
520
+
521
+ const visit = (id) => {
522
+ if (visited.has(id)) return;
523
+ visited.add(id);
524
+
525
+ // Visit dependencies first
526
+ for (const conn of this.connections) {
527
+ if (conn.to === id) {
528
+ visit(conn.from);
529
+ }
530
+ }
531
+
532
+ order.push(id);
533
+ };
534
+
535
+ for (const id of gateIds) {
536
+ visit(id);
537
+ }
538
+
539
+ return order;
540
+ }
541
+
542
+ /**
543
+ * Convert circuit to prime sequence representation
544
+ */
545
+ toPrimeCircuit() {
546
+ return {
547
+ gates: Object.fromEntries(
548
+ [...this.gates.entries()].map(([id, gate]) => [
549
+ id,
550
+ {
551
+ type: gate.constructor.name,
552
+ inputPrimes: gate.input1Primes || gate.inputPrimes,
553
+ outputPrimes: gate.outputPrimes
554
+ }
555
+ ])
556
+ ),
557
+ connections: this.connections
558
+ };
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Create common circuits
564
+ */
565
+ function createHalfAdder() {
566
+ const circuit = new DNACircuit('half-adder');
567
+
568
+ // Sum = A XOR B = (A AND NOT B) OR (NOT A AND B)
569
+ // Carry = A AND B
570
+
571
+ circuit.addGate('and1', new ANDGate({ name: 'carry' }));
572
+ circuit.addGate('not_a', new NOTGate({ name: 'not_a' }));
573
+ circuit.addGate('not_b', new NOTGate({ name: 'not_b' }));
574
+ circuit.addGate('and2', new ANDGate({ name: 'a_and_notb' }));
575
+ circuit.addGate('and3', new ANDGate({ name: 'nota_and_b' }));
576
+ circuit.addGate('or1', new ORGate({ name: 'sum' }));
577
+
578
+ // XOR construction
579
+ circuit.connect('not_a', 'and3', 1);
580
+ circuit.connect('not_b', 'and2', 2);
581
+ circuit.connect('and2', 'or1', 1);
582
+ circuit.connect('and3', 'or1', 2);
583
+
584
+ return circuit;
585
+ }
586
+
587
+ function createFullAdder() {
588
+ const halfAdder1 = createHalfAdder();
589
+ const halfAdder2 = createHalfAdder();
590
+
591
+ const circuit = new DNACircuit('full-adder');
592
+
593
+ // Full adder uses two half adders
594
+ // S = A XOR B XOR Cin
595
+ // Cout = (A AND B) OR (Cin AND (A XOR B))
596
+
597
+ // This is a simplified representation
598
+ circuit.addGate('xor1', new ANDGate({ name: 'xor1' })); // Simplified
599
+ circuit.addGate('xor2', new ANDGate({ name: 'xor2' }));
600
+ circuit.addGate('and1', new ANDGate({ name: 'and1' }));
601
+ circuit.addGate('and2', new ANDGate({ name: 'and2' }));
602
+ circuit.addGate('or1', new ORGate({ name: 'cout' }));
603
+
604
+ return circuit;
605
+ }
606
+
607
+ // ============================================================================
608
+ // Seesaw Gate (Qian & Winfree)
609
+ // ============================================================================
610
+
611
+ /**
612
+ * SeesawGate - DNA strand displacement seesaw gate
613
+ * Universal gate for digital circuit computation
614
+ */
615
+ class SeesawGate {
616
+ constructor(options = {}) {
617
+ this.name = options.name || 'seesaw';
618
+ this.threshold = options.threshold || 0.6;
619
+ this.gain = options.gain || 1.0;
620
+
621
+ // Gate state
622
+ this.inputWeights = options.weights || [1.0];
623
+ this.gateStrand = options.gateStrand || 'ATCGATCGATCG';
624
+ }
625
+
626
+ /**
627
+ * Evaluate seesaw gate
628
+ * Output = gain * (sum(weights * inputs) - threshold)
629
+ */
630
+ evaluate(inputs) {
631
+ const weightedSum = inputs.reduce((sum, inp, i) => {
632
+ return sum + (this.inputWeights[i] || 1.0) * inp;
633
+ }, 0);
634
+
635
+ const output = Math.max(0, this.gain * (weightedSum - this.threshold));
636
+
637
+ return {
638
+ output: Math.min(1, output),
639
+ active: weightedSum > this.threshold,
640
+ weightedSum
641
+ };
642
+ }
643
+ }
644
+
645
+ module.exports = {
646
+ // Strand classes
647
+ DNAStrand,
648
+ DNADuplex,
649
+
650
+ // Logic gates
651
+ ANDGate,
652
+ ORGate,
653
+ NOTGate,
654
+ NANDGate,
655
+ SeesawGate,
656
+
657
+ // Reactions
658
+ StrandDisplacementReaction,
659
+
660
+ // Circuits
661
+ DNACircuit,
662
+ createHalfAdder,
663
+ createFullAdder,
664
+ };