I know I can increase the size of the heap but that seems like a poor solution.
This program runs correctly on small files but when run on large data sets it crashes with the OutOfMemory: Java heap space error.
This code executes multiple hash map operations and I'm certain that the haphazard way in which I strung the data structures together is the root of this problem however I'm too inexperienced to think of a solution. I've never before had to think about runtime outside of answering questions about big O notation!
I'm trying to create a program to learn rules by inference, i.e. 'contains'('vitamin c', 'oranges').
, 'prevents'('scurvy', 'vitamin c').
would yield the output "rule" 'prevents'('scurvy', 'oranges').
I have code which will produce that output but then I wanted to eliminate duplicate "rules" from the input while keeping track of the number of times they were observed (as a naive confidence measure, since frequently observed rules are more likely to be true), so I implemented a hash map which stores the "rule" as a key and the number of observed instances as the value.
I've reproduced the code here, it's not that long.
Machine learning component architecture:
private List<Sentence> sentences = new ArrayList<>();
/*
* The following maps store the relation of a string occurring
* as a subject or object, respectively, to the list of Sentence
* ordinals where they occur.
*/
private Map<String,List<Integer>> subject2index = new HashMap<>();
private Map<String,List<Integer>> object2index = new HashMap<>();
/*
* This set contains strings that occur as both,
* subject and object. This is useful for determining strings
* acting as an in-between connecting two relations.
*/
private Set<String> joints = new HashSet<>();
public void addSentence( Sentence s )
{
// add Sentence to the list of all Sentences
sentences.add( s );
// add the Subject of the Sentence to the map mapping strings
// occurring as a subject to the ordinal of this Sentence
List<Integer> subind = subject2index.get( s.getSubject() );
if( subind == null )
{
subind = new ArrayList<>();
subject2index.put( s.getSubject(), subind );
}
subind.add( sentences.size() - 1 );
// add the Object of the Sentence to the map mapping strings
// occurring as an object to the ordinal of this Sentence
List<Integer> objind = object2index.get( s.getObject() );
if( objind == null )
{
objind = new ArrayList<>();
object2index.put( s.getObject(), objind );
}
objind.add( sentences.size() - 1 );
// determine whether we've found a "joining" string
if( subject2index.containsKey( s.getObject() ) )
{
joints.add( s.getObject() );
}
if( object2index.containsKey( s.getSubject() ) )
{
joints.add( s.getSubject() );
}
}
public Collection<String> getJoints()
{
return joints;
}
public List<Integer> getSubjectIndices( String subject )
{
return subject2index.get( subject );
}
public List<Integer> getObjectIndices( String object )
{
return object2index.get( object );
}
public Sentence getSentence( int index )
{
return sentences.get( index );
}
//map to store learned 'rules'
Map<Sentence, Integer> ruleCount = new HashMap<>();
//store data
public void numberRules(Sentence sentence)
{
if (!ruleCount.containsKey(sentence))
{
ruleCount.put(sentence, 0);
}
ruleCount.put(sentence, ruleCount.get(sentence) + 1);
}
Sentence Object:
public class Sentence
{
private String verb;
private String object;
private String subject;
public Sentence(String verb, String object, String subject )
{
this.verb = verb;
this.object = object;
this.subject = subject;
}
public String getVerb()
{
return verb;
}
public String getObject()
{
return object;
}
public String getSubject()
{
return subject;
}
public String toString()
{
return verb + "(" + object + ", " + subject + ").";
}
@Override
public boolean equals(Object other)
{
if (!(other instanceof Sentence))
return false;
if (other == this)
return true;
Sentence o = (Sentence) other;
return o.subject.equals(subject) && o.object.equals(object) && o.verb.equals(verb);
}
@Override
public int hashCode ()
{
return Objects.hash(object, subject, verb);
}
}
Code that executes:
public static void main(String[] args) throws IOException
{
Ontology ontology = new Ontology();
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
Pattern p = Pattern.compile("'(.*?)'\\('(.*?)',\\s*'(.*?)'\\)\\.");
String line;
while ((line = br.readLine()) != null)
{
Matcher m = p.matcher(line);
if( m.matches() )
{
String verb = m.group(1);
String object = m.group(2);
String subject = m.group(3);
ontology.addSentence( new Sentence( verb, object, subject ) );
}
}
for( String joint: ontology.getJoints() )
{
for( Integer subind: ontology.getSubjectIndices( joint ) )
{
Sentence xaS = ontology.getSentence( subind );
for( Integer obind: ontology.getObjectIndices( joint ) )
{
Sentence yOb = ontology.getSentence( obind );
Sentence s = new Sentence( xaS.getVerb(),
xaS.getObject(),
yOb.getSubject() );
//System.out.println( s );
ontology.numberRules( s );
}
}
}
for (Map.Entry<Sentence, Integer> entry : ontology.ruleCount.entrySet())
{
System.out.println(entry.getKey()+" : "+entry.getValue());
}
}
Input:
'prevents'('scurvy', 'vitamin C').
'contains'('vitamin C', 'orange').
'contains'('vitamin C', 'sauerkraut').
'is a'('fruit', 'orange').
'improves'('health', 'fruit').
'contains'('vitamin C', 'orange').
'improves'('health', 'fruit').
Output:
prevents(scurvy, orange). : 2
improves(health, orange). : 2
prevents(scurvy, sauerkraut). : 1
As an aside, is there a good way to store those "rules" so that the most frequently observed instances are on top?
PS: This is reposted from Stack Overflow.
Ontology
class, then I suggest that you edit your question to put them together. Also clean up on the formatting... :) \$\endgroup\$