Skip to content

Commit 064bbb5

Browse files
committed
add java implementation of HTM
1 parent dba2223 commit 064bbb5

File tree

11 files changed

+3322
-0
lines changed

11 files changed

+3322
-0
lines changed

src/HTM/java/RegionFrame.java

Lines changed: 547 additions & 0 deletions
Large diffs are not rendered by default.

src/HTM/java/RegionPanel.java

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
import htm.Cell;
2+
import htm.Column;
3+
import htm.Region;
4+
5+
import java.awt.Color;
6+
import java.awt.Dimension;
7+
import java.awt.Graphics;
8+
import java.awt.Point;
9+
10+
import javax.swing.BorderFactory;
11+
import javax.swing.JPanel;
12+
13+
/**
14+
* The RegionPanel is a JPanel that renders a visualization of an HTM Region.
15+
*/
16+
public class RegionPanel extends JPanel {
17+
18+
private static final long serialVersionUID = 1L;
19+
20+
private Region _region;
21+
private int _colRadius;
22+
private boolean _reInitBuffer;
23+
private boolean _showInput = true;
24+
private boolean _showInactive = true;
25+
private Point _mousePos = null;
26+
private int[][] _data = null;
27+
28+
private static final int COLUMN_BORDER = 3;
29+
private static final double SQRT2 = Math.sqrt(2.0);
30+
31+
/**
32+
* Construct a new RegionPanel that is to visualize the given Region.
33+
* @param region the Region to visualize.
34+
*/
35+
public RegionPanel(Region region) {
36+
super();
37+
setBorder(BorderFactory.createLineBorder(Color.black));
38+
_region = region;
39+
_reInitBuffer = true;
40+
}
41+
42+
/**
43+
* Initialize the bitmap used for buffering the display.
44+
*/
45+
private void initBuffer() {
46+
Dimension size = super.getSize();
47+
//compute circle radius for drawing region columns:
48+
int numXs = _region.getWidth();
49+
int numYs = _region.getHeight();
50+
51+
int bordersWidth = COLUMN_BORDER * (numXs+1);
52+
int bordersHeight = COLUMN_BORDER * (numYs+1);
53+
int maxRadWidth = (size.width - bordersWidth) / numXs;
54+
int maxRadHeight = (size.height - bordersHeight) / numYs;
55+
_colRadius = Math.min(maxRadWidth, maxRadHeight) / 2;
56+
57+
//Now create new bitmap same size as the window
58+
_reInitBuffer = false;
59+
}
60+
61+
@Override
62+
public Dimension getPreferredSize() {
63+
double ratio = _region.getWidth()*1.0 / _region.getHeight()*1.0;
64+
int maxWidth = Math.min(1000, 60*_region.getWidth()); //max 60pixels per col
65+
return new Dimension(maxWidth, Math.max(150, (int)(maxWidth/ratio)));
66+
}
67+
68+
@Override
69+
public void paintComponent(Graphics g) {
70+
super.paintComponent(g);
71+
72+
if(_reInitBuffer)
73+
initBuffer();
74+
75+
// First draw circles for columns same dimensions as region's col grid
76+
// Thick blue circles for active column, thin black for inactive
77+
// Fill circles with gray-scale of last input overlap %
78+
int rad = _colRadius;
79+
int rad2 = rad/2;
80+
int diam = rad*2;
81+
int rad2sqrt = (int)(rad2*1.0/SQRT2);
82+
83+
Dimension size = super.getSize();
84+
g.setColor(Color.WHITE);
85+
g.fillRect(0, 0, size.width, size.height);
86+
87+
for(int cx=0; cx<_region.getWidth(); ++cx) {
88+
for(int cy=0; cy<_region.getHeight(); ++cy) {
89+
Column col = _region.getColumn(cx, cy);
90+
Point circle = getColumnPos(col);
91+
92+
int gray = 255 - Math.round(col.getOverlapPercentage()*255.0f);
93+
if(gray<255)
94+
gray = gray;
95+
g.setColor(new Color(gray,gray,gray));
96+
g.fillOval(circle.x-rad, circle.y-rad, diam, diam);
97+
98+
boolean wasDrawn = false;
99+
if(col.isActive()) {
100+
wasDrawn = true;
101+
g.setColor(Color.BLUE);
102+
}
103+
else
104+
g.setColor(new Color(192,192,192));
105+
106+
g.drawOval(circle.x-rad, circle.y-rad, diam, diam);
107+
108+
//Now draw individual column cells inside the column's circle
109+
int radCell = rad2sqrt+1;
110+
int rad2C = rad2-1;
111+
if(_region.getCellsPerCol()==1) {
112+
radCell = rad-(rad/3);
113+
rad2C = 0;
114+
}
115+
116+
Point[] clocs = new Point[] {
117+
new Point(circle.x-rad2C, circle.y-rad2C),
118+
new Point(circle.x+rad2C, circle.y-rad2C),
119+
new Point(circle.x-rad2C, circle.y+rad2C),
120+
new Point(circle.x+rad2C, circle.y+rad2C)
121+
};
122+
123+
for(int i=0; i<col.numCells(); ++i)
124+
wasDrawn |= drawCell(g, clocs[i], radCell, col.getCell(i));
125+
126+
//Draw small black circle to indicate input bit origin locations
127+
if(_showInput && _data!=null && _data[col.ix()][col.iy()]==1) {
128+
wasDrawn = true;
129+
int r = Math.max(2, rad/6);
130+
g.setColor(new Color(128,0,128));
131+
g.fillOval(circle.x-r, circle.y-r, r*2, r*2);
132+
g.setColor(Color.BLACK);
133+
g.drawOval(circle.x-r, circle.y-r, r*2, r*2);
134+
}
135+
136+
//if column had no activity at all, hide it completely
137+
if(!_showInactive && !wasDrawn) {
138+
g.setColor(Color.WHITE);
139+
g.fillRect(circle.x-rad, circle.y-rad, diam+1,diam+1);
140+
}
141+
}
142+
}
143+
144+
//determine if a cell was clicked, if so draw its recentUpdateInfo
145+
}
146+
147+
// def draw(self, dc=None):
148+
// """ Draw the entire current state of the Region to the canvas. """
149+
//
150+
// #determine if a cell was clicked, if so draw its recentUpdateInfo
151+
// mouseCell = self.getMouseCell()
152+
// if mouseCell and mouseCell in self.region.recentUpdateMap:
153+
// segInfoList = self.region.recentUpdateMap[mouseCell]
154+
// for segInfo in segInfoList:
155+
// #draw red borders for new synapse-cells, purple for existing active
156+
// dc.SetPen(wx.Pen('BLACK', 1))
157+
// dc.SetBrush(wx.Brush('RED'))
158+
// for syn in segInfo.activeSynapses:
159+
// center = self.getCellPos(syn.inputSource)
160+
// dc.DrawCircle(center[0], center[1], radCell)
161+
//
162+
// dc.SetBrush(wx.Brush('PURPLE'))
163+
// for syn in segInfo.addedSynapses:
164+
// center = self.getCellPos(syn.inputSource)
165+
// dc.DrawCircle(center[0], center[1], radCell)
166+
//
167+
// dc.EndDrawing()
168+
//
169+
170+
/**
171+
* Get the pixel-position of the center of the column's circle.
172+
*/
173+
private Point getColumnPos(Column col) {
174+
int b = COLUMN_BORDER;
175+
int rad = _colRadius;
176+
int d = rad*2;
177+
int x = col.cx();
178+
int y = col.cy();
179+
return new Point((b*(x+1))+(d*x)+rad, (b*(y+1)) +(d*y)+rad);
180+
}
181+
182+
/**
183+
* Get the pixel-position of the center of the cell's circle.
184+
*/
185+
private Point getCellPos(Cell cell) {
186+
int rad = _colRadius;
187+
int rad2 = rad/2;
188+
Point circle = getColumnPos(cell.getColumn());
189+
int xr = rad2;
190+
int yr = rad2;
191+
int cellIndex = cell.getIndex();
192+
if(cellIndex==0 || cellIndex==2)
193+
xr = -rad2;
194+
if(cellIndex==0 || cellIndex==1)
195+
yr = -rad2;
196+
if(_region.getCellsPerCol()==1) { //special case for 1-cell
197+
xr = 0;
198+
yr = 0;
199+
}
200+
return new Point(circle.x+xr, circle.y+yr);
201+
}
202+
203+
/**
204+
* Return the last column cell the mouse clicked on, or None if the last
205+
* mouse click was not on a valid Cell position.
206+
*/
207+
private Cell getMouseCell() {
208+
if(_mousePos!=null) {
209+
int rad2sqrt = (int)(((double)(_colRadius/2)) / SQRT2);
210+
211+
int w = _colRadius*2 + COLUMN_BORDER;
212+
int x = _mousePos.x / w;
213+
int y = _mousePos.y / w;
214+
Column col = null;
215+
try { //if mouse clicked outside entire grid, return null
216+
col = _region.getColumn(x, y);
217+
}catch(ArrayIndexOutOfBoundsException ex) {
218+
return null;
219+
}
220+
221+
if(col.numCells()==1) //special case for 1-cell
222+
rad2sqrt = _colRadius-(_colRadius/3);
223+
224+
for(int i=0; i<col.numCells(); ++i) {
225+
Cell cell = col.getCell(i);
226+
Point center = getCellPos(cell);
227+
if(_mousePos.x > center.x-rad2sqrt && _mousePos.y > center.y-rad2sqrt) {
228+
if(_mousePos.x < center.x+rad2sqrt && _mousePos.y < center.y+rad2sqrt)
229+
return cell;
230+
}
231+
}
232+
}
233+
return null;
234+
}
235+
236+
/**
237+
* Draw an individual column cell with the center of the cell's
238+
* circle at the pixel location specified by 'center'.
239+
* @param dc: The wx DC the paint with.
240+
* @param center: the pixel location of the center of the cell's circle.
241+
* @param rad: the pixel radius of the cell's circle.
242+
* @param cell: the HTM Cell to draw.
243+
*/
244+
private boolean drawCell(Graphics g, Point center, int rad, Cell cell) {
245+
int cx = center.x;
246+
int cy = center.y;
247+
boolean wasDrawn = false;
248+
249+
//Filled green circles for predicting cells (yellow for non-sequence)
250+
if(cell.isPredicting()) {
251+
wasDrawn = true;
252+
if(cell.hasActiveSequenceSegment())
253+
g.setColor(Color.GREEN);
254+
else
255+
g.setColor(Color.YELLOW);
256+
g.fillOval(cx-rad, cy-rad, rad*2, rad*2);
257+
}
258+
259+
//Thick blue circles for active cells, otherwise thin black
260+
Color penColor = Color.WHITE;
261+
if(cell.isActive()) {
262+
wasDrawn = true;
263+
penColor = Color.BLUE;
264+
}
265+
else
266+
penColor = new Color(192,192,192);
267+
268+
g.setColor(penColor);
269+
g.drawOval(cx-rad, cy-rad, rad*2, rad*2);
270+
271+
//Vertical line inside circle for learning cells
272+
if(cell.isLearning()) {
273+
wasDrawn = true;
274+
g.setColor(Color.RED);
275+
g.drawLine(cx, cy-rad, cx, cy+rad);
276+
g.drawLine(cx-rad, cy, cx+rad, cy);
277+
}
278+
279+
//Check recentUpdateList and mark cells that had segments updated
280+
// if cell in self.region.recentUpdateMap:
281+
// #segInfo = self.region.recentUpdateMap[cell]
282+
// wasDrawn = True
283+
// dc.SetPen(wx.Pen('BLACK', 1))
284+
// dc.DrawLine(center[0], center[1]-radius, center[0], center[1]+radius)
285+
286+
return wasDrawn;
287+
}
288+
289+
}

src/HTM/java/htm/AbstractCell.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package htm;
2+
3+
/**
4+
* Represent an abstract HTM Cell. An abstract cell can have activation
5+
* and learning state, however in the simplest terms an abstract cell
6+
* represents an input source for segments. Since segments can be distal or
7+
* proximal we use this abstraction to refer to either of them at a base level.
8+
*/
9+
public abstract class AbstractCell {
10+
11+
/**
12+
* Return true if this Cell is active (false if not).
13+
*/
14+
public abstract boolean isActive();
15+
16+
/**
17+
* Return true if this Cell was active in the previous time step.
18+
*/
19+
public abstract boolean wasActive();
20+
21+
/**
22+
* Return true if this Cell was in the learning state in the previous
23+
* time step.
24+
*/
25+
public abstract boolean wasLearning();
26+
27+
/**
28+
* Return the x-coordinate representing the location of this Cell.
29+
* Often if this Cell is used by a proximal segment we need to know the
30+
* Cell's location to identify connection radius for the Column.
31+
*/
32+
public abstract int ix();
33+
34+
/**
35+
* Return the y-coordinate representing the location of this Cell.
36+
* Often if this Cell is used by a proximal segment we need to know the
37+
* Cell's location to identify connection radius for the Column.
38+
*/
39+
public abstract int iy();
40+
41+
/**
42+
* Return true if this Cell represents a Cell connected to distal segments
43+
* (as opposed to proximal segments connected to columns).
44+
*/
45+
public boolean isDistal() { return false; }
46+
47+
}

0 commit comments

Comments
 (0)